Skip to content

Conversation

@jzsfkzm
Copy link
Contributor

@jzsfkzm jzsfkzm commented Aug 13, 2025

refs #1024

made it a draft until #1766 is merged

Summary by CodeRabbit

  • New Features

    • Added WebSocket proxy endpoint (/ws) for streaming logs, events, shell, and ping/pong.
    • Switched provider interactions to JWT-based auth; no client certificates required.
    • Lease flow supports provider bid selection, deposits, updates, and graceful close.
  • Refactor

    • Simplified deployment and lease request payloads by removing certificate fields.
  • Documentation

    • Updated lease-flow example and step ordering to reflect the new certificate-free flow.
  • Tests

    • Added comprehensive unit and functional tests for JWT, provider interactions, and WebSocket proxy.

@jzsfkzm jzsfkzm requested a review from a team as a code owner August 13, 2025 09:44
@coderabbitai
Copy link

coderabbitai bot commented Aug 13, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Certificate-based flows are removed across deployment and lease operations. JWT-based provider communication and WebSocket proxying are added. Schemas and services are updated to use walletId instead of certificates. New telemetry helpers, shutdown utility, WebSocket routes/services, JWT token service, and HTTP SDK methods are introduced. Tests and setup utilities are added/refactored.

Changes

Cohort / File(s) Summary
Example flow update
apps/api/examples/lease-flow.ts
Removes certificate lifecycle; adds provider-specific bid selection; updates sequence (create deployment, bids, lease, deposit, update, close); adjusts payloads and logging; error handling improvements.
API server + WS enablement
apps/api/src/app.ts, apps/api/package.json
Adds WebSocket support (Hono WS), dynamic port init, returns AppServer with close(); integrates WS route; adds deps: @akashnetwork/jwt, @hono/node-ws, node-forge, typings.
Telemetry helpers
apps/api/src/core/lib/telemetry.ts
Adds traceActiveSpan and propagateTracingContext built on OpenTelemetry.
Schemas (certificate removal)
apps/api/src/deployment/http-schemas/deployment.schema.ts, apps/api/src/deployment/http-schemas/lease.schema.ts
Removes certificate fields from update-deployment and create-lease request schemas; only sdl/manifest+leases remain.
Deployment services (wallet-based)
apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts, apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts, apps/api/src/deployment/services/lease/lease.service.ts
Reader resolves wallet via UserWalletRepository; methods drop certificate options; statuses fetched via walletId. Writer and Lease services send manifests via ProviderService using walletId and call updated Reader.
Provider communication (JWT)
apps/api/src/provider/services/provider/provider.service.ts, packages/http-sdk/src/provider/provider-http.service.ts
Replaces proxy/cert with JWT-auth HTTP. sendManifest/getLeaseStatus now accept walletId, create JWT via JwtTokenService, and call HTTP SDK methods that set Bearer auth headers.
JWT token service and tests
apps/api/src/provider/services/jwt-token/jwt-token.service.ts, apps/api/src/provider/services/jwt-token/jwt-token.service.spec.ts
Adds JwtTokenService to generate signed JWTs with scoped/full-access leases; memoized per wallet. Comprehensive unit tests for TTL, memoization, jti uniqueness, and lease builders.
Wallet accessor
apps/api/src/billing/lib/wallet/wallet.ts
Adds getInstance() to expose DirectSecp256k1HdWallet.
Wallet initializer test updates
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts
Mocks findOneBy, integrates JwtTokenService mock, adjusts repository mocks and DI.
Shutdown helper
apps/api/src/lib/shutdown-server/shutdown-server.ts
Adds ClosableServer interface and shutdownServer(server) to gracefully close servers.
WebSocket stack (route, controller, services, schema, stats)
apps/api/src/websocket/...
Adds /ws route with upgradeWebSocket; WebsocketController handles lifecycle and dispatch; ProviderWebsocketService proxies to provider WS with JWT per URL scope; MESSAGE_SCHEMA validates messages; WebsocketStatsService tracks per-connection usage.
Functional tests (WS and flows)
apps/api/test/functional/websocket.spec.ts, apps/api/test/functional/lease-flow.spec.ts, apps/api/test/functional/deployments.spec.ts, apps/api/test/functional/balances.spec.ts
Adds WebSocket proxy tests; parameterizes lease lifecycle with/without certificate; updates deployment PUT tests for new schema and adds cert-included success path; consolidates balances setup.
Test setup utilities
apps/api/test/setup/server.ts, apps/api/test/setup/provider-server.ts, apps/api/test/setup/setup-user.ts, apps/api/test/seeders/x509-cert-pair.ts
Adds server start/stop, TLS provider server (HTTP+WS), reusable user setup/teardown with DI and nock mocks, and X509 cert pair seeder.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as API (Deployment/Lease)
  participant Reader as DeploymentReaderService
  participant Writer as DeploymentWriterService
  participant ProviderSvc as ProviderService
  participant Jwt as JwtTokenService
  participant HTTP as ProviderHttpService

  Client->>Writer: update({ sdl })
  Writer->>ProviderSvc: sendManifest({ provider, dseq, manifest, walletId })
  ProviderSvc->>Jwt: generateJwtToken({ walletId, leases: scoped(send-manifest) })
  Jwt-->>ProviderSvc: jwtToken
  ProviderSvc->>HTTP: sendManifest({ hostUri, dseq, manifest, jwtToken })
  HTTP-->>ProviderSvc: { success }
  ProviderSvc-->>Writer: ok
  Writer->>Reader: findByOwnerAndDseq(owner, dseq)
  Reader->>UserWalletRepo: find wallet by owner
  UserWalletRepo-->>Reader: wallet{id}
  Reader->>ProviderSvc: getLeaseStatus(..., walletId)
  ProviderSvc->>Jwt: generateJwtToken({ walletId, leases: scoped(status) })
  Jwt-->>ProviderSvc: jwtToken
  ProviderSvc->>HTTP: getLeaseStatus({ hostUri, dseq, gseq, oseq, jwtToken })
  HTTP-->>ProviderSvc: leaseStatus
  ProviderSvc-->>Reader: leaseStatus
  Reader-->>Writer: deploymentWithStatuses
  Writer-->>Client: response
Loading
sequenceDiagram
  autonumber
  participant Client as Browser WS
  participant API as /ws (Hono)
  participant Ctrl as WebsocketController
  participant ProvWS as ProviderWebsocketService
  participant Jwt as JwtTokenService
  participant Provider as Provider WS

  Client->>API: Connect /ws (Upgrade)
  API->>Ctrl: onOpen
  Client->>API: message {type:"ping"}
  API->>Ctrl: handleMessage
  Ctrl-->>Client: {type:"pong"}
  Client->>API: message {type:"websocket", url, data}
  API->>Ctrl: handleMessage
  Ctrl->>ProvWS: proxyMessageToProvider(message, ws, stats)
  ProvWS->>Jwt: generateJwtToken(scoped by url path)
  Jwt-->>ProvWS: jwtToken
  ProvWS->>Provider: Open WS with Bearer jwt
  ProvWS->>Provider: forward data
  Provider-->>ProvWS: messages/events
  ProvWS-->>Client: proxy responses
  Client-->>API: close
  API->>Ctrl: handleClose
  Ctrl->>ProvWS: closeProviderSocket
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90 minutes

Possibly related PRs

Suggested reviewers

  • stalniy
  • baktun14
  • ygrishajev

Poem

A hop, a bop, I sign with JWT,
No leafy certs, just wallet and me.
I tunnel logs through webs that sing,
With pings that pong and tokens that ring.
Deploy I shall, ears high, bright-eyed—
Carrots committed, changes applied! 🥕✨

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

"dependencies": {
"@akashnetwork/akash-api": "^1.3.0",
"@akashnetwork/akashjs": "^0.11.1",
"@akashnetwork/database": "*",
Copy link
Contributor

@github-actions github-actions bot Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔄 Carefully review the package-lock.json diff

Resolve the comment if everything is ok

* node_modules/@types/ws                                                                   8.5.13 -> 8.18.1
+ node_modules/@hono/node-ws                                                               1.2.0   
+ node_modules/wait-for-expect                                                             4.0.0   

@jzsfkzm jzsfkzm marked this pull request as draft August 13, 2025 09:44
@socket-security
Copy link

socket-security bot commented Aug 13, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​hono/​node-ws@​1.2.0981007985100

View full report

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 23

🔭 Outside diff range comments (3)
apps/api/examples/lease-flow.ts (2)

101-113: Remove hardcoded provider; allow TARGET_PROVIDER override and fallback to first bid

Hardcoding a provider makes the example brittle. Prefer env var override and a safe fallback.

-// Select a bid from a specific provider
-const targetProvider = "akash175llqyjvxfle9qwt740vm46772dzaznpzgm576";
-const selectedBid = bids.find(bid => bid.bid.bid_id.provider === targetProvider);
+// Select a bid: use TARGET_PROVIDER if set, otherwise take the first bid
+const targetProvider = process.env.TARGET_PROVIDER;
+const selectedBid = targetProvider
+  ? bids.find(bid => bid.bid.bid_id.provider === targetProvider)
+  : bids[0];
 
 if (!selectedBid) {
-  throw new Error(`No bid found from provider ${targetProvider}`);
+  throw new Error(
+    targetProvider
+      ? `No bid found from provider ${targetProvider}`
+      : "No bids available to select"
+  );
 }

Additionally, consider preferring cheapest/fastest bids if business logic permits.


101-105: Type the bids instead of using any to comply with TS guidelines

The current code (waitForBids and downstream) uses any[]. Define a minimal Bid type to avoid any and tighten usage.

Below type additions are outside the changed range—add them near the imports:

// Minimal shape used by this script; extend if needed
type BidId = { gseq: number; oseq: number; provider: string };
type BidItem = { bid: { bid_id: BidId } };
type BidsResponse = { data?: BidItem[] };

Then update waitForBids signature and response handling:

-async function waitForBids(dseq: string, apiKey: string, maxAttempts = 10): Promise<any[]> {
+async function waitForBids(dseq: string, apiKey: string, maxAttempts = 10): Promise<BidItem[]> {
   for (let i = 0; i < maxAttempts; i++) {
     try {
-      const response = await api.get(`/v1/bids?dseq=${dseq}`, {
+      const response = await api.get<BidsResponse>(`/v1/bids?dseq=${dseq}`, {
         headers: {
           "x-api-key": apiKey
         }
       });
 
-      if (response.data?.data?.length > 0) {
-        return response.data.data;
+      if ((response.data?.data?.length ?? 0) > 0) {
+        return response.data.data!;
       }
     } catch (error) {
       console.error("Error waiting for bids:", error);
     }

Optionally, add axios timeouts for resiliency (outside changed range):

// When creating axios instance:
const api = axios.create({
  baseURL: API_URL,
  timeout: 30_000,
  headers: { "Content-Type": "application/json" }
});
apps/api/test/functional/lease-flow.spec.ts (1)

256-256: Update deployment always includes certificate regardless of test parameter

The update deployment request on line 256 always includes the certificate ({ certPem, keyPem: encryptedKey }), even when the test is running with includeCertificate: false. This inconsistency could mask issues in the certificate-free flow.

Apply this fix to maintain consistency with the test parameter:

-      body: JSON.stringify({ data: { sdl: ymlUpdate, certificate: { certPem, keyPem: encryptedKey } } })
+      body: JSON.stringify({ 
+        data: { 
+          sdl: ymlUpdate, 
+          certificate: includeCertificate 
+            ? { certPem, keyPem: encryptedKey } 
+            : undefined 
+        } 
+      })
🧹 Nitpick comments (24)
apps/api/examples/lease-flow.ts (2)

5-11: Fix doc steps to match actual behavior and make provider selection configurable

The comments say “first received bid,” but code later selects a specific hard-coded provider. Clarify docs and make selection configurable via env, falling back to the first bid.

- * 4. Creates a lease using the first received bid
+ * 4. Creates a lease using a chosen provider (via TARGET_PROVIDER env var) or falls back to the first received bid

69-72: Documentation block below should reflect JWT/cert-less flow and error handling

Minor doc polish: the script creates a lease, deposits, updates, fetches details, then closes. Also note it’s certificate-less.

- * It creates a deployment, waits for bids, creates a lease, and then closes the deployment.
+ * It creates a deployment, waits for bids, creates a lease (JWT-based, no certificates), deposits, updates the deployment, fetches details, and then closes the deployment.
apps/api/src/websocket/services/websocket-stats/websocket-stats.service.ts (1)

22-24: Do not expose internal array; return a shallow copy to avoid external mutation

Returning the internal array reference allows external code to observe in-place mutations. Return a copy for safety.

-  getItems(): ReadonlyArray<ClientWebSocketStats> {
-    return this.items;
-  }
+  getItems(): ReadonlyArray<ClientWebSocketStats> {
+    return [...this.items];
+  }
apps/api/src/billing/lib/wallet/wallet.ts (1)

50-52: Type annotate return and avoid unnecessary await in getInstance

Return the underlying promise directly and add an explicit return type for clarity and to avoid the extra microtask hop.

Apply this diff:

-  async getInstance() {
-    return await this.instanceAsPromised;
-  }
+  async getInstance(): Promise<DirectSecp256k1HdWallet> {
+    return this.instanceAsPromised;
+  }
apps/api/src/lib/shutdown-server/shutdown-server.ts (1)

1-12: LGTM; minor: drop async when manually constructing a Promise

Function already returns a Promise; removing async simplifies without behavioral change.

Apply this diff:

-export async function shutdownServer(server: ClosableServer | undefined): Promise<void> {
+export function shutdownServer(server: ClosableServer | undefined): Promise<void> {
   if (!server || !server.listening) return;
   return new Promise<void>((resolve, reject) => {
     server.close(error => {
       if (error) {
         reject(error);
       } else {
         resolve();
       }
     });
   });
 }
apps/api/src/websocket/routes/websocket/websocket.router.ts (1)

21-24: Propagate tracing context on open for consistency

Other handlers are wrapped in propagateTracingContext. Do the same for onOpen to ensure span continuity.

Apply this diff:

-        onOpen: async (_event: Event, wsContext: WSContext<unknown>) => {
-          trackingSpan?.setAttribute("ws.url", wsContext.url?.toString() ?? "");
-          controller.handleOpen(stats);
-        },
+        onOpen: async (_event: Event, wsContext: WSContext<unknown>) => {
+          trackingSpan?.setAttribute("ws.url", wsContext.url?.toString() ?? "");
+          propagateTracingContext(() => {
+            controller.handleOpen(stats);
+          })();
+        },
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts (3)

5-5: JwtTokenService mock is not utilized in the tests

The JwtTokenService is imported and mocked with generateJwtToken, but it doesn't appear to be invoked by WalletInitializerService in these test scenarios. Consider removing this mock if it's not needed.

If the service is not used, remove the unnecessary mock:

-import { JwtTokenService } from "@src/provider/services/jwt-token/jwt-token.service";
-    di.registerInstance(
-      JwtTokenService,
-      mock<JwtTokenService>({
-        generateJwtToken: jest.fn().mockResolvedValue("mock-jwt-token")
-      })
-    );

Also applies to: 119-124


19-19: findWalletBy mock is defined but never verified in tests

The findWalletBy function is mocked and passed to the setup, but none of the test cases verify that findOneBy is actually called. This mock appears to be unused.

Either add test assertions to verify this method is called, or remove the unused mock if it's not needed:

-      const findWalletBy = jest.fn().mockImplementation(async () => newWallet);
       const findWalletByUserId = jest.fn().mockImplementation(async () => null);
       // ... other mocks
       const di = setup({
-        findWalletBy,
         findWalletByUserId,
         // ... other setup params
       });

Also update the SetupInput interface:

   interface SetupInput {
-    findWalletBy?: UserWalletRepository["findOneBy"];
     findWalletByUserId?: UserWalletRepository["findOneByUserId"];
     // ... other fields
   }

Also applies to: 26-26, 132-132


90-139: Move setup function to the bottom of the describe block

According to the coding guidelines, the setup function should be at the bottom of the root describe block. Currently, it's placed after the test cases but should be moved to the end of the describe block.

Move the setup function and SetupInput interface after line 88 (end of the last test) but before the closing brace of the describe block on line 139.

apps/api/test/functional/lease-flow.spec.ts (1)

117-131: Consider using a more descriptive structure for test cases

The test parameterization using forEach with an array of objects is good, but could be more maintainable with a dedicated test cases structure.

Consider extracting test cases to a more readable structure:

-  [
-    {
-      name: "should execute complete lease lifecycle without a certificate provided",
-      includeCertificate: false
-    },
-    {
-      name: "should execute complete lease lifecycle with a certificate provided",
-      includeCertificate: true
-    }
-  ].forEach(({ name, includeCertificate }) => {
-    it(name, async () => {
-      await runLifecycle(includeCertificate);
-    });
-  });
+  const testCases = [
+    { includeCertificate: false, description: "without a certificate provided" },
+    { includeCertificate: true, description: "with a certificate provided" }
+  ];
+
+  testCases.forEach(({ includeCertificate, description }) => {
+    it(`should execute complete lease lifecycle ${description}`, async () => {
+      await runLifecycle(includeCertificate);
+    });
+  });
apps/api/src/deployment/services/lease/lease.service.ts (1)

33-33: Consider adding error handling for manifest sending failures

The manifest sending could fail for individual providers, but the current implementation doesn't handle these failures. Consider whether partial failures should be logged or if the entire operation should be rolled back.

Consider collecting errors and handling them appropriately:

+    const manifestErrors: Array<{ provider: string; error: unknown }> = [];
     for (const lease of input.leases) {
-      await this.providerService.sendManifest({ provider: lease.provider, dseq: lease.dseq, manifest: input.manifest, walletId: wallet.id });
+      try {
+        await this.providerService.sendManifest({ 
+          provider: lease.provider, 
+          dseq: lease.dseq, 
+          manifest: input.manifest, 
+          walletId: wallet.id 
+        });
+      } catch (error) {
+        manifestErrors.push({ provider: lease.provider, error });
+      }
     }
+    
+    if (manifestErrors.length > 0) {
+      // Log errors or throw based on business requirements
+      console.error('Failed to send manifest to some providers:', manifestErrors);
+    }
apps/api/test/functional/balances.spec.ts (2)

92-94: Move setup function to the bottom of the describe block

According to the coding guidelines, the setup function should be at the bottom of the root describe block, after all test cases.

Move the setup function after line 90 (the last test) but before the closing brace of the describe block on line 95:

   it("should return 404 when user wallet is not found", async () => {
     // ... test code
   });

+  async function setup(options?: SetupUserOptions) {
+    return await setupUser(options);
+  }
 });
-
-  async function setup(options?: SetupUserOptions) {
-    return await setupUser(options);
-  }

16-19: Consider consolidating cleanup logic

Having both afterEach and afterAll with identical cleanup code seems redundant. The afterEach should be sufficient for cleaning up after each test.

Consider removing the redundant afterAll block:

   afterEach(() => {
     jest.restoreAllMocks();
     nock.cleanAll();
   });

-  afterAll(async () => {
-    jest.restoreAllMocks();
-    nock.cleanAll();
-  });
apps/api/src/websocket/http-schemas/websocket.schema.ts (1)

23-25: Consider improving the data field description

The description for the data field mentions implementation details about char codes and array representation, which might change. Consider a more abstract description.

     data: z
       .string()
       .optional()
-      .describe(
-        "Currently it's used only for service shell communication and stores only buffered representation of string in char codes something like this: Array.from(Uint8Array).join(', ')"
-      )
+      .describe("Optional data payload for the WebSocket message")
packages/http-sdk/src/provider/provider-http.service.ts (1)

36-41: Headers helper is fine; optional: add Accept.

The helper looks good. Optionally add Accept: application/json for completeness across providers.

apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts (1)

288-295: Consider making wallet lookup non-throwing for read paths.

Optional: have this helper return null and let callers decide whether to degrade or error. Keeps public read endpoints from coupling to wallet presence.

Proposed alternative:

-  private async getWalletByAddress(address: string) {
-    const wallet = await this.userWalletRepository.findOneBy({ address });
-    if (!wallet) {
-      throw new Error(`Wallet not found for address: ${address}`);
-    }
-    return wallet;
-  }
+  private async getWalletByAddress(address: string) {
+    return await this.userWalletRepository.findOneBy({ address }) ?? null;
+  }

Then gate usage at call sites as shown in prior comments.

apps/api/src/app.ts (1)

210-216: Minor: redundant string type check on port.

initApp’s port is typed as number with a numeric default. The runtime typeof port === "string" branch is dead. Simplify.

-    const server = serve({
-      fetch: appHono.fetch,
-      port: typeof port === "string" ? parseInt(port, 10) : port
-    });
+    const server = serve({
+      fetch: appHono.fetch,
+      port
+    });
apps/api/test/seeders/x509-cert-pair.ts (1)

40-44: Optional: expose validity window helper for reuse.

If other seeders need rolling validity, consider exporting nextDay or allow a validityDays option.

apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts (1)

126-131: Consider bounded parallelism for manifest delivery to multiple providers.

Sequential sends can be slow with multiple leases; unbounded parallelism can overload the provider(s). Use limited concurrency.

Example with Promise.allSettled and a simple concurrency gate:

-    for (const provider of leaseProviders) {
-      await this.providerService.sendManifest({ provider, dseq, manifest, walletId });
-    }
+    const tasks = leaseProviders.map(p => async () => this.providerService.sendManifest({ provider: p, dseq, manifest, walletId }));
+    const concurrency = 5;
+    for (let i = 0; i < tasks.length; i += concurrency) {
+      const batch = tasks.slice(i, i + concurrency).map(fn => fn());
+      await Promise.allSettled(batch);
+    }

This balances throughput and resilience without failing the whole update on a single provider error.

apps/api/test/functional/websocket.spec.ts (1)

26-26: Remove console.log statement from test

Debug console.log statement should be removed from the test code.

-          console.log("messages", messages);
apps/api/test/setup/provider-server.ts (1)

22-22: Use const instead of let for cleanupHandlers

Since cleanupHandlers is reassigned rather than mutated, using const would be cleaner and prevent accidental mutations.

-    let cleanupHandlers = new Set<() => void>();
+    const cleanupHandlers = new Set<() => void>();

And update the clear operation:

     server.on("close", () => {
       cleanupHandlers.forEach(handler => handler());
-      cleanupHandlers = new Set();
+      cleanupHandlers.clear();
     });
apps/api/src/websocket/controllers/websocket/websocket.controller.ts (1)

133-133: Remove unused parameter prefix

The _event parameter name uses underscore prefix to indicate it's unused, but the parameter itself should be typed properly for documentation purposes.

-  async handleClose(_event: any, stats: ClientWebSocketStats, span?: Span) {
+  async handleClose(_event: CloseEvent, stats: ClientWebSocketStats, span?: Span) {
apps/api/test/setup/setup-user.ts (1)

73-78: Extract mock function to avoid duplication

The findOneByUserIdMock function is defined but could be more concise by directly returning the implementation.

-    const findOneByUserIdMock = jest.fn().mockImplementation(async (id: string) => {
-      if (id === userWithId.id) {
-        return options?.walletNotFound ? undefined : wallet;
-      }
-      return undefined;
-    });
-
     jest.spyOn(userWalletRepository, "accessibleBy").mockReturnValue(userWalletRepository);
-    jest.spyOn(userWalletRepository, "findOneByUserId").mockImplementation(findOneByUserIdMock);
+    jest.spyOn(userWalletRepository, "findOneByUserId").mockImplementation(async (id: string) => {
+      if (id === userWithId.id) {
+        return options?.walletNotFound ? undefined : wallet;
+      }
+      return undefined;
+    });
apps/api/src/provider/services/provider/provider.service.ts (1)

31-32: Consider validating manifest structure before string replacement

The code performs a string replacement on the manifest JSON without validation. This could potentially cause issues if the manifest structure changes or contains unexpected patterns.

Consider parsing and validating the manifest before manipulation:

   async sendManifest({ provider, dseq, manifest, walletId }: { provider: string; dseq: string; manifest: string; walletId: number }) {
-    const manifestWithSize = manifest.replace(/"quantity":{"val/g, '"size":{"val');
+    // Parse, validate, and transform the manifest properly
+    let manifestObj;
+    try {
+      manifestObj = JSON.parse(manifest);
+      // Transform quantity to size in the parsed object
+      const transformedManifest = JSON.stringify(manifestObj).replace(/"quantity":{"val/g, '"size":{"val');
+      const manifestWithSize = transformedManifest;
+    } catch (e) {
+      throw new Error('Invalid manifest JSON');
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 070910e and 9f8b4c9.

⛔ Files ignored due to path filters (2)
  • apps/api/test/functional/__snapshots__/docs.spec.ts.snap is excluded by !**/*.snap
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (30)
  • apps/api/examples/lease-flow.ts (9 hunks)
  • apps/api/package.json (5 hunks)
  • apps/api/src/app.ts (5 hunks)
  • apps/api/src/billing/lib/wallet/wallet.ts (1 hunks)
  • apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts (3 hunks)
  • apps/api/src/core/lib/telemetry.ts (1 hunks)
  • apps/api/src/deployment/http-schemas/deployment.schema.ts (1 hunks)
  • apps/api/src/deployment/http-schemas/lease.schema.ts (0 hunks)
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts (5 hunks)
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts (3 hunks)
  • apps/api/src/deployment/services/lease/lease.service.ts (1 hunks)
  • apps/api/src/lib/shutdown-server/shutdown-server.ts (1 hunks)
  • apps/api/src/provider/services/jwt-token/jwt-token.service.spec.ts (1 hunks)
  • apps/api/src/provider/services/jwt-token/jwt-token.service.ts (1 hunks)
  • apps/api/src/provider/services/provider/provider.service.spec.ts (1 hunks)
  • apps/api/src/provider/services/provider/provider.service.ts (3 hunks)
  • apps/api/src/websocket/controllers/websocket/websocket.controller.ts (1 hunks)
  • apps/api/src/websocket/http-schemas/websocket.schema.ts (1 hunks)
  • apps/api/src/websocket/routes/websocket/websocket.router.ts (1 hunks)
  • apps/api/src/websocket/services/provider-websocket/provider-websocket.service.ts (1 hunks)
  • apps/api/src/websocket/services/websocket-stats/websocket-stats.service.ts (1 hunks)
  • apps/api/test/functional/balances.spec.ts (2 hunks)
  • apps/api/test/functional/deployments.spec.ts (7 hunks)
  • apps/api/test/functional/lease-flow.spec.ts (3 hunks)
  • apps/api/test/functional/websocket.spec.ts (1 hunks)
  • apps/api/test/seeders/x509-cert-pair.ts (1 hunks)
  • apps/api/test/setup/provider-server.ts (1 hunks)
  • apps/api/test/setup/server.ts (1 hunks)
  • apps/api/test/setup/setup-user.ts (1 hunks)
  • packages/http-sdk/src/provider/provider-http.service.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/api/src/deployment/http-schemas/lease.schema.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/test/setup/server.ts
  • apps/api/src/websocket/http-schemas/websocket.schema.ts
  • apps/api/src/core/lib/telemetry.ts
  • apps/api/src/lib/shutdown-server/shutdown-server.ts
  • apps/api/src/deployment/http-schemas/deployment.schema.ts
  • apps/api/src/websocket/controllers/websocket/websocket.controller.ts
  • apps/api/test/functional/deployments.spec.ts
  • packages/http-sdk/src/provider/provider-http.service.ts
  • apps/api/src/websocket/routes/websocket/websocket.router.ts
  • apps/api/src/provider/services/provider/provider.service.spec.ts
  • apps/api/test/setup/setup-user.ts
  • apps/api/test/functional/websocket.spec.ts
  • apps/api/src/app.ts
  • apps/api/src/provider/services/jwt-token/jwt-token.service.spec.ts
  • apps/api/src/billing/lib/wallet/wallet.ts
  • apps/api/test/seeders/x509-cert-pair.ts
  • apps/api/src/deployment/services/lease/lease.service.ts
  • apps/api/src/websocket/services/websocket-stats/websocket-stats.service.ts
  • apps/api/test/setup/provider-server.ts
  • apps/api/examples/lease-flow.ts
  • apps/api/test/functional/lease-flow.spec.ts
  • apps/api/src/websocket/services/provider-websocket/provider-websocket.service.ts
  • apps/api/src/provider/services/jwt-token/jwt-token.service.ts
  • apps/api/test/functional/balances.spec.ts
  • apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts
  • apps/api/src/provider/services/provider/provider.service.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/test/setup/server.ts
  • apps/api/src/websocket/http-schemas/websocket.schema.ts
  • apps/api/src/core/lib/telemetry.ts
  • apps/api/src/lib/shutdown-server/shutdown-server.ts
  • apps/api/src/deployment/http-schemas/deployment.schema.ts
  • apps/api/src/websocket/controllers/websocket/websocket.controller.ts
  • apps/api/test/functional/deployments.spec.ts
  • packages/http-sdk/src/provider/provider-http.service.ts
  • apps/api/src/websocket/routes/websocket/websocket.router.ts
  • apps/api/src/provider/services/provider/provider.service.spec.ts
  • apps/api/test/setup/setup-user.ts
  • apps/api/test/functional/websocket.spec.ts
  • apps/api/src/app.ts
  • apps/api/src/provider/services/jwt-token/jwt-token.service.spec.ts
  • apps/api/src/billing/lib/wallet/wallet.ts
  • apps/api/test/seeders/x509-cert-pair.ts
  • apps/api/src/deployment/services/lease/lease.service.ts
  • apps/api/src/websocket/services/websocket-stats/websocket-stats.service.ts
  • apps/api/test/setup/provider-server.ts
  • apps/api/examples/lease-flow.ts
  • apps/api/test/functional/lease-flow.spec.ts
  • apps/api/src/websocket/services/provider-websocket/provider-websocket.service.ts
  • apps/api/src/provider/services/jwt-token/jwt-token.service.ts
  • apps/api/test/functional/balances.spec.ts
  • apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts
  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts
  • apps/api/src/provider/services/provider/provider.service.ts
**/*.spec.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() to mock dependencies in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test.

**/*.spec.{ts,tsx}: Use setup function instead of beforeEach in test files
setup function must be at the bottom of the root describe block in test files
setup function creates an object under test and returns it
setup function should accept a single parameter with inline type definition
Don't use shared state in setup function
Don't specify return type of setup function

Files:

  • apps/api/test/functional/deployments.spec.ts
  • apps/api/src/provider/services/provider/provider.service.spec.ts
  • apps/api/test/functional/websocket.spec.ts
  • apps/api/src/provider/services/jwt-token/jwt-token.service.spec.ts
  • apps/api/test/functional/lease-flow.spec.ts
  • apps/api/test/functional/balances.spec.ts
  • apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts
🧠 Learnings (3)
📚 Learning: 2025-07-21T08:25:07.474Z
Learnt from: CR
PR: akash-network/console#0
File: .cursor/rules/setup-instead-of-before-each.mdc:0-0
Timestamp: 2025-07-21T08:25:07.474Z
Learning: Applies to **/*.spec.{ts,tsx} : `setup` function creates an object under test and returns it

Applied to files:

  • apps/api/test/setup/setup-user.ts
  • apps/api/test/functional/balances.spec.ts
📚 Learning: 2025-07-21T08:25:07.474Z
Learnt from: CR
PR: akash-network/console#0
File: .cursor/rules/setup-instead-of-before-each.mdc:0-0
Timestamp: 2025-07-21T08:25:07.474Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `setup` function instead of `beforeEach` in test files

Applied to files:

  • apps/api/test/functional/balances.spec.ts
📚 Learning: 2025-06-03T15:06:34.211Z
Learnt from: baktun14
PR: akash-network/console#1428
File: apps/api/src/deployment/controllers/deployment/deployment.controller.ts:0-0
Timestamp: 2025-06-03T15:06:34.211Z
Learning: The `getByOwnerAndDseq` method in `apps/api/src/deployment/controllers/deployment/deployment.controller.ts` is intentionally public without the `Protected` decorator because it serves public blockchain data from an indexer, following the pattern of public blockchain APIs.

Applied to files:

  • apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts
🧬 Code Graph Analysis (19)
apps/api/test/setup/server.ts (1)
apps/api/src/app.ts (2)
  • AppServer (255-258)
  • initApp (205-231)
apps/api/src/websocket/http-schemas/websocket.schema.ts (3)
apps/provider-proxy/src/utils/schema.ts (1)
  • chainNetworkSchema (15-15)
packages/net/src/index.ts (1)
  • netConfig (3-3)
apps/provider-proxy/src/utils/isValidBech32.ts (1)
  • isValidBech32Address (3-10)
apps/api/src/core/lib/telemetry.ts (1)
apps/api/src/core/services/execution-context/execution-context.service.ts (1)
  • context (18-26)
apps/api/src/websocket/controllers/websocket/websocket.controller.ts (5)
apps/api/src/provider/services/jwt-token/jwt-token.service.ts (1)
  • singleton (24-67)
apps/api/src/websocket/services/websocket-stats/websocket-stats.service.ts (3)
  • singleton (7-25)
  • ClientWebSocketStats (27-94)
  • WebSocketUsage (96-96)
apps/api/src/websocket/services/provider-websocket/provider-websocket.service.ts (1)
  • singleton (22-224)
apps/api/src/core/lib/telemetry.ts (1)
  • traceActiveSpan (5-7)
apps/api/src/websocket/http-schemas/websocket.schema.ts (2)
  • WsMessage (29-29)
  • MESSAGE_SCHEMA (13-27)
apps/api/test/functional/deployments.spec.ts (1)
apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts (1)
  • UserWalletOutput (16-20)
apps/api/src/websocket/routes/websocket/websocket.router.ts (1)
apps/api/src/core/lib/telemetry.ts (1)
  • propagateTracingContext (9-12)
apps/api/src/provider/services/provider/provider.service.spec.ts (1)
packages/http-sdk/src/provider/provider-http.service.ts (1)
  • ProviderHttpService (6-42)
apps/api/test/setup/setup-user.ts (6)
apps/api/test/services/wallet-testing.service.ts (1)
  • WalletTestingService (13-150)
apps/api/test/services/stub.ts (1)
  • stub (1-1)
apps/api/src/utils/constants.ts (1)
  • apiNodeUrl (19-19)
apps/api/test/seeders/fee-allowance-response.seeder.ts (1)
  • FeeAllowanceResponseSeeder (10-32)
apps/api/test/seeders/deployment-grant-response.seeder.ts (1)
  • DeploymentGrantResponseSeeder (10-36)
apps/api/test/seeders/deployment-list-response.seeder.ts (1)
  • DeploymentListResponseSeeder (6-19)
apps/api/test/functional/websocket.spec.ts (4)
apps/api/test/setup/provider-server.ts (2)
  • stopProviderServer (68-70)
  • startProviderServer (14-66)
apps/api/test/setup/setup-user.ts (1)
  • setupUser (26-140)
apps/api/test/setup/server.ts (1)
  • startServer (6-10)
apps/api/test/seeders/x509-cert-pair.ts (1)
  • createX509CertPair (4-26)
apps/api/src/app.ts (2)
apps/api/src/websocket/routes/websocket/websocket.router.ts (1)
  • initLeaseWebsocketRoute (11-50)
apps/api/src/core/services/shutdown-server/shutdown-server.ts (1)
  • shutdownServer (7-33)
apps/api/src/provider/services/jwt-token/jwt-token.service.spec.ts (2)
apps/api/src/billing/providers/config.provider.ts (1)
  • BillingConfig (9-9)
apps/api/src/billing/lib/wallet/wallet.ts (1)
  • Wallet (7-53)
apps/api/src/deployment/services/lease/lease.service.ts (1)
apps/indexer/drizzle/schema.ts (1)
  • lease (110-178)
apps/api/test/setup/provider-server.ts (2)
apps/api/test/seeders/x509-cert-pair.ts (2)
  • createX509CertPair (4-26)
  • CertPair (28-31)
apps/api/src/lib/shutdown-server/shutdown-server.ts (1)
  • shutdownServer (1-12)
apps/api/src/websocket/services/provider-websocket/provider-websocket.service.ts (7)
apps/api/src/websocket/http-schemas/websocket.schema.ts (1)
  • WsMessage (29-29)
apps/api/src/provider/services/jwt-token/jwt-token.service.ts (1)
  • singleton (24-67)
apps/api/src/provider/services/provider/provider.service.ts (1)
  • singleton (19-225)
apps/api/src/websocket/controllers/websocket/websocket.controller.ts (1)
  • singleton (14-148)
apps/api/src/websocket/services/websocket-stats/websocket-stats.service.ts (2)
  • singleton (7-25)
  • ClientWebSocketStats (27-94)
apps/api/src/core/lib/telemetry.ts (1)
  • propagateTracingContext (9-12)
packages/logging/src/servicies/logger/logger.service.ts (1)
  • error (107-109)
apps/api/src/provider/services/jwt-token/jwt-token.service.ts (3)
apps/api/src/billing/providers/config.provider.ts (2)
  • InjectBillingConfig (11-11)
  • BillingConfig (9-9)
apps/api/src/caching/helpers.ts (1)
  • Memoize (22-38)
apps/api/src/billing/lib/wallet/wallet.ts (1)
  • Wallet (7-53)
apps/api/test/functional/balances.spec.ts (1)
apps/api/test/setup/setup-user.ts (2)
  • SetupUserOptions (19-21)
  • setupUser (26-140)
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts (2)
apps/api/src/billing/services/managed-user-wallet/managed-user-wallet.service.ts (2)
  • createWallet (59-69)
  • createAndAuthorizeTrialSpending (44-57)
apps/deploy-web/src/services/auth/auth.service.ts (1)
  • AuthService (5-21)
apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts (2)
apps/indexer/drizzle/schema.ts (3)
  • deployment (180-203)
  • lease (110-178)
  • provider (205-237)
apps/api/src/deployment/http-schemas/deployment.schema.ts (1)
  • GetDeploymentResponse (248-248)
apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts (2)
apps/api/src/deployment/http-schemas/deployment.schema.ts (1)
  • GetDeploymentResponse (248-248)
apps/indexer/drizzle/schema.ts (2)
  • lease (110-178)
  • deployment (180-203)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: validate (apps/stats-web) / validate-unsafe
  • GitHub Check: validate (apps/notifications) / validate-unsafe
  • GitHub Check: validate (apps/deploy-web) / validate-unsafe
  • GitHub Check: validate (apps/api) / validate-unsafe
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: Validate local packages
🔇 Additional comments (19)
apps/api/package.json (1)

45-45: Pin and audit WebSocket & JWT dependencies

  • Replace "*" for @akashnetwork/jwt with an exact version (or use workspace:*) to ensure reproducible builds and limit supply-chain risk. Verify how this package is being resolved (public registry vs. private registry or local workspace).
  • Pin a specific version of @hono/node-ws, confirm your supported Node.js versions, and ensure it doesn’t inadvertently pull in multiple WS implementations.
  • If node-forge is only used for generating test certificates (per the PR summary), move it to devDependencies to minimize your production attack surface.
  • Audit both node-forge and ws for known security advisories and apply any necessary patches or upgrades.

Applies to apps/api/package.json at lines 45, 60, 94, 130, and 154.

apps/api/test/functional/deployments.spec.ts (7)

71-74: Confirm repository query shape for findOneBy spy

The spy assumes queries will always include address and returns a wallet by address. If production code calls findOneBy with other fields (e.g., userId), this will return undefined and tests may diverge from production behavior.

If findOneBy is used with multiple fields, extend the predicate to match all provided fields defensively.


117-118: LGTM: Consolidating wallets for findOneBy mock

Collecting wallets in allWallets to back findOneBy simplifies the mocking story and reduces duplication.


694-711: LGTM: PUT update now aligns with wallet-based flow (no certs)

Payload includes only sdl, matching the updated schema and flow. Assertions remain correct.


713-749: Validate schema compatibility when including certificate in payload

Update schema reportedly dropped certificate fields. Including certificate here may fail validation if unknown keys are rejected. If the API tolerates extraneous fields (strip/ignore), this is fine; otherwise, consider removing certificate from this test or updating the schema to allow it where intended.

Would you like me to scan the schema to confirm whether unknown keys are stripped or cause 400s and adjust the test accordingly?


764-764: LGTM: NotFound path updated to the new request shape

Using only sdl in the payload keeps the test aligned with the simplified schema.


785-786: LGTM: 401 path updated to the new request shape

Ensures unauthenticated requests are validated against the simplified payload.


809-810: LGTM: Validation path targets invalid SDL only

The negative test aligns with the schema change by focusing solely on SDL invalidation.

apps/api/test/functional/lease-flow.spec.ts (1)

196-201: Certificate data might be used even when includeCertificate is false

When includeCertificate is false, the certificate field is set to undefined, but certPem and encryptedKey are still extracted from the certificate response at line 156. This data exists but isn't being used, which could be confusing.

The conditional certificate inclusion logic is correct and aligns with the PR's goal of supporting both certificate-based and wallet-based authentication paths.

apps/api/src/deployment/services/lease/lease.service.ts (1)

1-39: LGTM! Clean migration to wallet-based authentication

The service has been successfully updated to use wallet-based authentication instead of certificates. The changes are well-structured and maintain clear separation of concerns.

apps/api/src/websocket/http-schemas/websocket.schema.ts (2)

1-30: LGTM! Well-structured WebSocket schema definitions

The schema definitions are clean and properly typed with Zod. The discriminated union pattern for message types provides good type safety.


6-6: chainNetworkSchema is safe – netConfigData always contains at least one network
The netConfigData object is generated at build time and must include at least one network key, so
getSupportedNetworks() (which does Object.keys(netConfigData)) can never return an empty array. No guard or schema change is required here.

apps/api/src/deployment/services/deployment-reader/deployment-reader.service.ts (2)

40-60: Good per-lease error isolation in status lookup.
Your try/catch ensures a single provider failure doesn’t break the whole response for findByOwnerAndDseq. Keep this pattern.


97-105: Guard against undefined indexes when wiring leases to statuses.

If any lease list failed or had fewer leases than expected, direct indexing may throw. Use nullish coalescing.

Apply:

-    const deploymentsWithLeases = deployments.map((deployment, deploymentIndex) => ({
+    const deploymentsWithLeases = deployments.map((deployment, deploymentIndex) => ({
       deployment: deployment.deployment,
-      leases:
-        leaseResults[deploymentIndex]?.leases?.map(({ lease }, leaseIndex) => ({
+      leases:
+        (leaseResultsOrdered[deploymentIndex]?.leases ?? []).map(({ lease }, leaseIndex) => ({
           ...lease,
-          status: leaseStatuses[deploymentIndex][leaseIndex]
+          status: leaseStatuses[deploymentIndex]?.[leaseIndex] ?? null
         })) ?? [],
       escrow_account: deployment.escrow_account
     }));

Please verify the surrounding identifiers (leaseResultsOrdered) if you adopt the previous suggestion.

apps/api/src/app.ts (2)

166-168: WS wiring looks correct.

createNodeWebSocket + injectWebSocket(server) pattern is correctly applied, and the route initialization is clean.


221-226: AppServer shape is solid; graceful shutdown is clear.

The close() correctly reuses the centralized shutdown helper and disposes the DI container.

apps/api/test/seeders/x509-cert-pair.ts (1)

4-26: Test certificate generator LGTM.

Sane defaults, correct RSA keypair and self-signed X.509 creation. Suitable for TLS-enabled test servers.

apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts (1)

95-107: Update flow refactor looks correct; manifest delivery uses walletId.

Validation, version check, sendManifest with walletId, and final readback are coherent.

apps/api/src/provider/services/jwt-token/jwt-token.service.ts (1)

1-67: LGTM! Clean JWT token service implementation

The implementation correctly handles JWT token generation with proper memoization, TTL management, and lease payload construction. The service follows best practices and integrates well with the wallet infrastructure.

Comment on lines +5 to +12
export function traceActiveSpan<T extends (span: Span) => any>(name: string, callback: T): ReturnType<T> {
return trace.getTracer("default").startActiveSpan(name, callback);
}

export function propagateTracingContext<T extends (...args: any[]) => any>(callback: T): T {
const currentContext = context.active();
return ((...args) => context.with(currentContext, () => callback(...args))) as T;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid any in TS and auto-end spans to prevent leaks; preserve this binding when propagating context

  • Coding guideline violation: use of any in generics.
  • startActiveSpan variant requires you to end spans; failing to do so risks memory leaks and broken traces.
  • Preserve callable this when propagating context.
-export function traceActiveSpan<T extends (span: Span) => any>(name: string, callback: T): ReturnType<T> {
-  return trace.getTracer("default").startActiveSpan(name, callback);
-}
+export function traceActiveSpan<R>(name: string, callback: (span: Span) => R): R {
+  return trace.getTracer("default").startActiveSpan(name, span => {
+    try {
+      return callback(span);
+    } finally {
+      // Ensure spans are ended even if callback throws
+      span.end();
+    }
+  });
+}
 
-export function propagateTracingContext<T extends (...args: any[]) => any>(callback: T): T {
-  const currentContext = context.active();
-  return ((...args) => context.with(currentContext, () => callback(...args))) as T;
-}
+export function propagateTracingContext<T extends (...args: unknown[]) => unknown>(callback: T): T {
+  const currentContext = context.active();
+  return (function (this: ThisParameterType<T>, ...args: Parameters<T>): ReturnType<T> {
+    return context.with(currentContext, () => callback.apply(this, args)) as ReturnType<T>;
+  } as unknown) as T;
+}

Note: If callers currently call span.end() manually, double end is a no-op in OTel SDKs; still, consider removing redundant ends where feasible.

🤖 Prompt for AI Agents
In apps/api/src/core/lib/telemetry.ts around lines 5 to 12, the functions use
"any" in generics, startActiveSpan is used without ensuring spans are ended
(risking leaks), and propagateTracingContext loses the caller's this binding;
change the generic signatures to avoid any (use proper callable types or unknown
for arguments and explicit return types), update traceActiveSpan to wrap the
provided callback so the span is ended automatically after the callback finishes
(ensuring span.end() is always called even on errors), and change
propagateTracingContext to preserve this by returning a function with an
explicit this parameter (e.g., function(this: unknown, ...args) { return
context.with(currentContext, () => callback.apply(this, args)); }) so the
original this is forwarded while keeping strong types.

Comment on lines 102 to 106
export const UpdateDeploymentRequestSchema = z.object({
data: z.object({
sdl: z.string(),
certificate: z.object({
certPem: z.string(),
keyPem: z.string()
})
sdl: z.string()
})
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Tighten SDL validation (min length, sensible size cap, OpenAPI metadata)

The update payload now only carries SDL, which is good. Add basic constraints and docs to prevent empty/oversized bodies and improve API docs.

 export const UpdateDeploymentRequestSchema = z.object({
   data: z.object({
-    sdl: z.string()
+    sdl: z
+      .string()
+      .min(1, "SDL is required")
+      .max(1_000_000, "SDL too large (max 1MB)")
+      .openapi({
+        description: "Akash SDL manifest (YAML)",
+        example: "services:\n  web:\n    image: nginx\n    expose:\n      - port: 80"
+      })
   })
 });

If 1MB is too restrictive/lenient for your providers, adjust accordingly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const UpdateDeploymentRequestSchema = z.object({
data: z.object({
sdl: z.string(),
certificate: z.object({
certPem: z.string(),
keyPem: z.string()
})
sdl: z.string()
})
});
export const UpdateDeploymentRequestSchema = z.object({
data: z.object({
sdl: z
.string()
.min(1, "SDL is required")
.max(1_000_000, "SDL too large (max 1MB)")
.openapi({
description: "Akash SDL manifest (YAML)",
example: "services:\n web:\n image: nginx\n expose:\n - port: 80"
})
})
});
🤖 Prompt for AI Agents
In apps/api/src/deployment/http-schemas/deployment.schema.ts around lines
102–106, tighten the SDL field by enforcing a minimum length and a sensible
maximum (e.g. .min(1) after trimming and .max(1_048_576) for ~1MB), and add
OpenAPI metadata (description and example) so generated docs reflect the
constraints; implement trimming before length checks (or use a refine to reject
blank-only strings) and prefer numeric constant for the max size so it’s easy to
adjust.

Comment on lines +28 to 31
public async findByOwnerAndDseq(owner: string, dseq: string): Promise<GetDeploymentResponse["data"]> {
const wallet = await this.getWalletByAddress(owner);
const deploymentResponse = await this.deploymentHttpService.findByOwnerAndDseq(owner, dseq);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t hard-require a wallet for public read endpoints; degrade gracefully.

Per prior design, these endpoints serve public indexer data. Throwing when a wallet isn’t found will break read-only requests for arbitrary owners. Fallback to “status: null” when no wallet exists.

Apply this diff to gracefully handle missing wallets:

-  public async findByOwnerAndDseq(owner: string, dseq: string): Promise<GetDeploymentResponse["data"]> {
-    const wallet = await this.getWalletByAddress(owner);
+  public async findByOwnerAndDseq(owner: string, dseq: string): Promise<GetDeploymentResponse["data"]> {
+    let walletId: number | null = null;
+    try {
+      const wallet = await this.getWalletByAddress(owner);
+      walletId = wallet.id;
+    } catch {
+      walletId = null;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public async findByOwnerAndDseq(owner: string, dseq: string): Promise<GetDeploymentResponse["data"]> {
const wallet = await this.getWalletByAddress(owner);
const deploymentResponse = await this.deploymentHttpService.findByOwnerAndDseq(owner, dseq);
public async findByOwnerAndDseq(owner: string, dseq: string): Promise<GetDeploymentResponse["data"]> {
let walletId: number | null = null;
try {
const wallet = await this.getWalletByAddress(owner);
walletId = wallet.id;
} catch {
walletId = null;
}
const deploymentResponse = await this.deploymentHttpService.findByOwnerAndDseq(owner, dseq);

Comment on lines +86 to +95
const wallet = await this.getWalletByAddress(owner);
const leaseStatuses = await Promise.all(
leaseResults.map(async ({ leases }) => {
return await Promise.all(
leases.map(async ({ lease }) => {
return await this.providerService.getLeaseStatus(lease.lease_id.provider, lease.lease_id.dseq, lease.lease_id.gseq, lease.lease_id.oseq, wallet.id);
})
);
})
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Index misalignment and error handling can break listing; preserve order and isolate failures.

PromisePool.results may be shorter than deployments on failures, breaking index-based assembly. Also, provider status retrieval lacks error handling and will reject the whole request on a single failure.

Refactor to preserve order and isolate failures:

-    const wallet = await this.getWalletByAddress(owner);
-    const leaseStatuses = await Promise.all(
-      leaseResults.map(async ({ leases }) => {
-        return await Promise.all(
-          leases.map(async ({ lease }) => {
-            return await this.providerService.getLeaseStatus(lease.lease_id.provider, lease.lease_id.dseq, lease.lease_id.gseq, lease.lease_id.oseq, wallet.id);
-          })
-        );
-      })
-    );
+    // Wallet is optional: if not found, fall back to null statuses (public read)
+    let walletId: number | null = null;
+    try {
+      const wallet = await this.getWalletByAddress(owner);
+      walletId = wallet.id;
+    } catch {
+      walletId = null;
+    }
+
+    // Preserve index ordering even when some lease list calls fail
+    const leaseListResults = await Promise.allSettled(
+      deployments.map(d => this.leaseHttpService.list({ owner, dseq: d.deployment.deployment_id.dseq }))
+    );
+    const leaseResultsOrdered = leaseListResults.map(r =>
+      r.status === "fulfilled" ? r.value : { leases: [] as typeof r extends never ? never : any[] } // keep shape
+    );
+
+    // Compute statuses per lease with failure isolation and optional wallet
+    const leaseStatuses = await Promise.all(
+      leaseResultsOrdered.map(async ({ leases }) => {
+        return Promise.all(
+          leases.map(async ({ lease }) => {
+            if (walletId == null) return null;
+            try {
+              return await this.providerService.getLeaseStatus(
+                lease.lease_id.provider,
+                lease.lease_id.dseq,
+                lease.lease_id.gseq,
+                lease.lease_id.oseq,
+                walletId
+              );
+            } catch {
+              return null;
+            }
+          })
+        );
+      })
+    );

Notes:

  • allSettled keeps array length = deployments.length.
  • Each provider call is wrapped with try/catch; failures become null.
  • If wallet doesn’t exist, all statuses are null instead of throwing.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +124 to +130
function setup(): {
jwtTokenService: JwtTokenService;
mockBillingConfig: MockProxy<BillingConfig>;
mockWalletId: number;
mockWallet: MockProxy<WalletModule.Wallet>;
leases: JwtTokenPayload["leases"];
} {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove explicit return type from setup function

According to the coding guidelines, don't specify the return type of the setup function.

-  function setup(): {
-    jwtTokenService: JwtTokenService;
-    mockBillingConfig: MockProxy<BillingConfig>;
-    mockWalletId: number;
-    mockWallet: MockProxy<WalletModule.Wallet>;
-    leases: JwtTokenPayload["leases"];
-  } {
+  function setup() {
🤖 Prompt for AI Agents
In apps/api/src/provider/services/jwt-token/jwt-token.service.spec.ts around
lines 124 to 130, the setup function currently declares an explicit return type;
remove the ": { jwtTokenService: JwtTokenService; mockBillingConfig:
MockProxy<BillingConfig>; mockWalletId: number; mockWallet:
MockProxy<WalletModule.Wallet>; leases: JwtTokenPayload['leases']; }" annotation
so the function relies on TypeScript's type inference — simply declare "function
setup()" (keeping the same body) and let the compiler infer the return shape.

Comment on lines 165 to 193
type SetupOptions = {
onConnection?: (providerWs: WebSocket) => void;
};

async function setup(options: SetupOptions) {
const { apiKey } = await setupUser();

const proxyServerUrl = await startServer();
const providerAddress = createAkashAddress();
const certPair = createX509CertPair({ commonName: providerAddress });
const { providerUrl } = await startProviderServer({
certPair,
websocketServer: {
enable: true,
onConnection: options.onConnection
}
});
const ws = new WebSocket(`${proxyServerUrl}/ws`, {
headers: {
"x-api-key": apiKey
}
});

return {
providerAddress,
providerUrl,
ws
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Setup function should be at the bottom of the root describe block

According to the coding guidelines, the setup function must be at the bottom of the root describe block.

Move the setup function and helper functions to the bottom:

 describe("Provider WebSocket", () => {
   afterEach(() => {
     stopProviderServer();
     teardownUser();
   });

   it("proxies provider websocket messages", async () => {
     // ... test
   });

   it("responds to ping messages", async () => {
     // ... test
   });

   // ... other tests
+
+  function providerMessage<T>(message: T, extra?: Record<string, any>) {
+    return {
+      ...extra,
+      type: "websocket",
+      message
+    };
+  }
+
+  function ourMessage(message: string, url: string, extra?: Record<string, any>) {
+    return {
+      ...extra,
+      type: "websocket",
+      data: message
+        .split("")
+        .map(char => char.charCodeAt(0))
+        .join(","),
+      url: `${url}/test`,
+      providerAddress: extra?.providerAddress || createAkashAddress(),
+      chainNetwork: extra?.chainNetwork || "sandbox"
+    };
+  }
+
+  function waitForMessage<T>(ws: WebSocket) {
+    return new Promise<T>(resolve => {
+      ws.once("message", data => resolve(JSON.parse(data.toString())));
+    });
+  }
+
+  type SetupOptions = {
+    onConnection?: (providerWs: WebSocket) => void;
+  };
+
+  async function setup(options: SetupOptions) {
+    const { apiKey } = await setupUser();
+
+    const proxyServerUrl = await startServer();
+    const providerAddress = createAkashAddress();
+    const certPair = createX509CertPair({ commonName: providerAddress });
+    const { providerUrl } = await startProviderServer({
+      certPair,
+      websocketServer: {
+        enable: true,
+        onConnection: options.onConnection
+      }
+    });
+    const ws = new WebSocket(`${proxyServerUrl}/ws`, {
+      headers: {
+        "x-api-key": apiKey
+      }
+    });
+
+    return {
+      providerAddress,
+      providerUrl,
+      ws
+    };
+  }
-
-  function providerMessage<T>(message: T, extra?: Record<string, any>) {
-    // ... implementation
-  }
-
-  function ourMessage(message: string, url: string, extra?: Record<string, any>) {
-    // ... implementation
-  }
-
-  function waitForMessage<T>(ws: WebSocket) {
-    // ... implementation
-  }
-
-  type SetupOptions = {
-    onConnection?: (providerWs: WebSocket) => void;
-  };
-
-  async function setup(options: SetupOptions) {
-    // ... implementation
-  }
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type SetupOptions = {
onConnection?: (providerWs: WebSocket) => void;
};
async function setup(options: SetupOptions) {
const { apiKey } = await setupUser();
const proxyServerUrl = await startServer();
const providerAddress = createAkashAddress();
const certPair = createX509CertPair({ commonName: providerAddress });
const { providerUrl } = await startProviderServer({
certPair,
websocketServer: {
enable: true,
onConnection: options.onConnection
}
});
const ws = new WebSocket(`${proxyServerUrl}/ws`, {
headers: {
"x-api-key": apiKey
}
});
return {
providerAddress,
providerUrl,
ws
};
}
describe("Provider WebSocket", () => {
afterEach(() => {
stopProviderServer();
teardownUser();
});
it("proxies provider websocket messages", async () => {
// ... test
});
it("responds to ping messages", async () => {
// ... test
});
// ... other tests
function providerMessage<T>(message: T, extra?: Record<string, any>) {
return {
...extra,
type: "websocket",
message
};
}
function ourMessage(message: string, url: string, extra?: Record<string, any>) {
return {
...extra,
type: "websocket",
data: message
.split("")
.map(char => char.charCodeAt(0))
.join(","),
url: `${url}/test`,
providerAddress: extra?.providerAddress || createAkashAddress(),
chainNetwork: extra?.chainNetwork || "sandbox"
};
}
function waitForMessage<T>(ws: WebSocket) {
return new Promise<T>(resolve => {
ws.once("message", data => resolve(JSON.parse(data.toString())));
});
}
type SetupOptions = {
onConnection?: (providerWs: WebSocket) => void;
};
async function setup(options: SetupOptions) {
const { apiKey } = await setupUser();
const proxyServerUrl = await startServer();
const providerAddress = createAkashAddress();
const certPair = createX509CertPair({ commonName: providerAddress });
const { providerUrl } = await startProviderServer({
certPair,
websocketServer: {
enable: true,
onConnection: options.onConnection
}
});
const ws = new WebSocket(`${proxyServerUrl}/ws`, {
headers: {
"x-api-key": apiKey
}
});
return {
providerAddress,
providerUrl,
ws
};
}
});
🤖 Prompt for AI Agents
In apps/api/test/functional/websocket.spec.ts around lines 165 to 193, the setup
helper is defined mid-file but must be placed at the bottom of the root describe
block per guidelines; move the entire setup function (and any related helper
functions) so they appear after all tests and nested describes, i.e., append
them just before the file ends within the root describe block, update any
references/imports if necessary, and ensure no tests rely on hoisting behavior —
run the test suite to confirm nothing breaks.

Comment on lines 169 to 193
async function setup(options: SetupOptions) {
const { apiKey } = await setupUser();

const proxyServerUrl = await startServer();
const providerAddress = createAkashAddress();
const certPair = createX509CertPair({ commonName: providerAddress });
const { providerUrl } = await startProviderServer({
certPair,
websocketServer: {
enable: true,
onConnection: options.onConnection
}
});
const ws = new WebSocket(`${proxyServerUrl}/ws`, {
headers: {
"x-api-key": apiKey
}
});

return {
providerAddress,
providerUrl,
ws
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Setup function should accept inline type definition directly

According to the coding guidelines, the setup function should accept a single parameter with inline type definition, not reference an external type.

-  type SetupOptions = {
-    onConnection?: (providerWs: WebSocket) => void;
-  };
-
-  async function setup(options: SetupOptions) {
+  async function setup(options: {
+    onConnection?: (providerWs: WebSocket) => void;
+  }) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function setup(options: SetupOptions) {
const { apiKey } = await setupUser();
const proxyServerUrl = await startServer();
const providerAddress = createAkashAddress();
const certPair = createX509CertPair({ commonName: providerAddress });
const { providerUrl } = await startProviderServer({
certPair,
websocketServer: {
enable: true,
onConnection: options.onConnection
}
});
const ws = new WebSocket(`${proxyServerUrl}/ws`, {
headers: {
"x-api-key": apiKey
}
});
return {
providerAddress,
providerUrl,
ws
};
}
async function setup(options: {
onConnection?: (providerWs: WebSocket) => void;
}) {
const { apiKey } = await setupUser();
const proxyServerUrl = await startServer();
const providerAddress = createAkashAddress();
const certPair = createX509CertPair({ commonName: providerAddress });
const { providerUrl } = await startProviderServer({
certPair,
websocketServer: {
enable: true,
onConnection: options.onConnection
}
});
const ws = new WebSocket(`${proxyServerUrl}/ws`, {
headers: {
"x-api-key": apiKey
}
});
return {
providerAddress,
providerUrl,
ws
};
}
🤖 Prompt for AI Agents
In apps/api/test/functional/websocket.spec.ts around lines 169-193, the setup
function currently types its parameter as SetupOptions; replace that external
type reference with an inline parameter type (a single object whose properties
include an optional onConnection callback that receives the new
websocket/connection), update the function signature to use that inline shape,
and remove any now-unused SetupOptions import/definition so the file compiles
and conforms to the coding guideline.

import { createAkashAddress } from "@test/seeders";
import { type CertPair, createX509CertPair } from "@test/seeders/x509-cert-pair";

let runningServer: https.Server | undefined;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential memory leak with global server reference

The runningServer variable is set but may not be cleared after shutdown, potentially causing issues if multiple tests run in sequence.

Clear the reference after shutdown:

 export function stopProviderServer(): Promise<void> {
-  return shutdownServer(runningServer);
+  const serverToShutdown = runningServer;
+  runningServer = undefined;
+  return shutdownServer(serverToShutdown);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let runningServer: https.Server | undefined;
export function stopProviderServer(): Promise<void> {
const serverToShutdown = runningServer;
runningServer = undefined;
return shutdownServer(serverToShutdown);
}
🤖 Prompt for AI Agents
In apps/api/test/setup/provider-server.ts around line 12 the module-level
variable `runningServer` may hold a Server instance after shutdown causing a
potential memory leak; after calling server.close()/awaiting its close callback,
explicitly set `runningServer = undefined` (and similarly clear any other global
refs) so repeated test runs don’t retain the old server; update the
teardown/stop logic to await close and then null out the variable (and ensure
any error paths also clear it).

Comment on lines +6 to +10
export async function startServer(): Promise<string> {
server = await initApp(0);

return server!.host;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make server lifecycle idempotent and clear the handle after stop

  • Prevent multiple running instances when startServer is called twice.
  • Ensure the module-scoped handle is cleared to avoid leaking stale state between tests.

Apply this diff:

 export async function startServer(): Promise<string> {
-  server = await initApp(0);
-
-  return server!.host;
+  if (server) {
+    await server.close();
+    server = undefined;
+  }
+  server = await initApp(0);
+  return server.host;
 }
 
 export async function stopServer(): Promise<void> {
-  await server?.close();
+  if (server) {
+    await server.close();
+    server = undefined;
+  }
 }

Also applies to: 12-14

🤖 Prompt for AI Agents
In apps/api/test/setup/server.ts around lines 6-10 and 12-14, make startServer
idempotent and clear the module-scoped handle on stop: if a server handle
already exists, return its host instead of creating a new server; when
stopServer is called, await shutdown and set the module-scoped server variable
to null/undefined to avoid leaking stale state between tests. Ensure startServer
initializes only when server is falsy and stopServer always clears the handle
after successful stop (and is safe to call if server is already null).

Comment on lines +15 to +23
async sendManifest({ hostUri, dseq, manifest, jwtToken }: { hostUri: string; dseq: string; manifest: string; jwtToken: string }) {
return this.extractData(
await this.put(`/deployment/${dseq}/manifest`, {
baseURL: hostUri,
body: manifest,
headers: this.getJwtTokenHeaders(jwtToken),
timeout: 60000
})
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

PUT payload not sent: use Axios data arg (or config.data) instead of body.

HttpService is Axios-based (constructor typed with AxiosRequestConfig). Axios ignores body in config and expects the payload as the second argument or in config.data. As written, the manifest won’t be sent.

Apply one of these fixes (preferred: pass manifest as second argument):

-  async sendManifest({ hostUri, dseq, manifest, jwtToken }: { hostUri: string; dseq: string; manifest: string; jwtToken: string }) {
-    return this.extractData(
-      await this.put(`/deployment/${dseq}/manifest`, {
-        baseURL: hostUri,
-        body: manifest,
-        headers: this.getJwtTokenHeaders(jwtToken),
-        timeout: 60000
-      })
-    );
-  }
+  async sendManifest({ hostUri, dseq, manifest, jwtToken }: { hostUri: string; dseq: string; manifest: string; jwtToken: string }) {
+    return this.extractData(
+      await this.put(`/deployment/${dseq}/manifest`, 
+        manifest,
+        {
+          baseURL: hostUri,
+          headers: this.getJwtTokenHeaders(jwtToken),
+          timeout: 60000
+        }
+      )
+    );
+  }

Alternatively, if HttpService.put only accepts a single config object, put the payload in data:

-        body: manifest,
+        data: manifest,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async sendManifest({ hostUri, dseq, manifest, jwtToken }: { hostUri: string; dseq: string; manifest: string; jwtToken: string }) {
return this.extractData(
await this.put(`/deployment/${dseq}/manifest`, {
baseURL: hostUri,
body: manifest,
headers: this.getJwtTokenHeaders(jwtToken),
timeout: 60000
})
);
async sendManifest(
{ hostUri, dseq, manifest, jwtToken }: { hostUri: string; dseq: string; manifest: string; jwtToken: string }
) {
return this.extractData(
await this.put(
`/deployment/${dseq}/manifest`,
manifest,
{
baseURL: hostUri,
headers: this.getJwtTokenHeaders(jwtToken),
timeout: 60000
}
)
);
}
🤖 Prompt for AI Agents
In packages/http-sdk/src/provider/provider-http.service.ts around lines 15 to
23, the PUT request currently places the manifest into a non‑existent `body`
config key so Axios will not send the payload; change the call to pass the
manifest as the request body (preferred: pass manifest as the second argument to
this.put and keep the config object for baseURL, headers and timeout) or, if
this.put only accepts a single config object, move the payload into `data` on
the config; ensure headers (getJwtTokenHeaders), baseURL and timeout remain in
the config and then return the result via extractData as before.

@jzsfkzm jzsfkzm force-pushed the features/1024-stream-lease-logs branch from 9f8b4c9 to 9ef22b8 Compare August 13, 2025 20:32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: we could add an example on how to pull the logs or events

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added an example, not 100% sure about it though, can I run it locally?

@jzsfkzm jzsfkzm force-pushed the features/1024-stream-lease-logs branch 2 times, most recently from f38b451 to fbbac53 Compare August 25, 2025 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants