@science-corporation/synapse 2.2.5 → 2.2.7

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.
@@ -453,6 +453,10 @@
453
453
  "responseType": "AppDeployResponse",
454
454
  "responseStream": true
455
455
  },
456
+ "ListApps": {
457
+ "requestType": "ListAppsRequest",
458
+ "responseType": "ListAppsResponse"
459
+ },
456
460
  "ListFiles": {
457
461
  "requestType": "google.protobuf.Empty",
458
462
  "responseType": "ListFilesResponse"
@@ -1416,6 +1420,30 @@
1416
1420
  }
1417
1421
  }
1418
1422
  },
1423
+ "AppInfo": {
1424
+ "fields": {
1425
+ "name": {
1426
+ "type": "string",
1427
+ "id": 1
1428
+ },
1429
+ "version": {
1430
+ "type": "string",
1431
+ "id": 2
1432
+ }
1433
+ }
1434
+ },
1435
+ "ListAppsRequest": {
1436
+ "fields": {}
1437
+ },
1438
+ "ListAppsResponse": {
1439
+ "fields": {
1440
+ "apps": {
1441
+ "rule": "repeated",
1442
+ "type": "AppInfo",
1443
+ "id": 1
1444
+ }
1445
+ }
1446
+ },
1419
1447
  "FunctionProfile": {
1420
1448
  "fields": {
1421
1449
  "name": {
package/src/device.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import { Channel, credentials, ServiceError } from "@grpc/grpc-js";
2
+ import * as fs from "fs";
3
+ import * as crypto from "crypto";
4
+ import * as path from "path";
5
+ import Long from "long";
2
6
 
3
7
  import protos from "./api/proto.json";
4
8
  import { synapse } from "./api/api";
@@ -6,6 +10,24 @@ import Config from "./config";
6
10
  import { CallOptions, create } from "./utils/client";
7
11
  import { fromDeviceStatus, Status } from "./utils/status";
8
12
 
13
+ const FILE_CHUNK_SIZE = 1024 * 1024; // 1MB chunks
14
+
15
+ function calculateSha256(filePath: string): Promise<string> {
16
+ return new Promise((resolve, reject) => {
17
+ const hash = crypto.createHash("sha256");
18
+ const stream = fs.createReadStream(filePath);
19
+ stream.on("data", (data) => hash.update(data));
20
+ stream.on("end", () => resolve(hash.digest("hex")));
21
+ stream.on("error", reject);
22
+ });
23
+ }
24
+
25
+ function extractVersion(packageName: string): string {
26
+ // Format: package-name_version_architecture.deb
27
+ const parts = packageName.split("_");
28
+ return parts.length >= 2 ? parts[1] : "";
29
+ }
30
+
9
31
  const kSynapseService = "synapse.SynapseDevice";
10
32
 
11
33
  class Device {
@@ -183,6 +205,124 @@ class Device {
183
205
  return call;
184
206
  }
185
207
 
208
+ // Apps
209
+
210
+ async listApps(options: CallOptions = {}): Promise<{ status: Status; response?: synapse.ListAppsResponse }> {
211
+ return new Promise((resolve, reject) => {
212
+ this.rpc.listApps({}, options, (err: ServiceError, res: synapse.ListAppsResponse) => {
213
+ if (err) {
214
+ reject(err);
215
+ } else if (!res) {
216
+ reject(new Error("No response from listApps"));
217
+ } else {
218
+ resolve({ status: new Status(), response: res });
219
+ }
220
+ });
221
+ });
222
+ }
223
+
224
+ deployApp(
225
+ options: CallOptions = {},
226
+ callbacks: {
227
+ onData: (data: synapse.AppDeployResponse) => void;
228
+ onEnd?: () => void;
229
+ onError?: (err: Error) => void;
230
+ onStatus?: (status: Status) => void;
231
+ }
232
+ ) {
233
+ const { onData, onEnd, onError, onStatus } = callbacks;
234
+ const call = this.rpc.deployApp({}, options);
235
+
236
+ call.on("data", (data: synapse.AppDeployResponse) => {
237
+ onData(data);
238
+ });
239
+ if (onEnd) {
240
+ call.on("end", onEnd);
241
+ }
242
+ if (onError) {
243
+ call.on("error", (err: ServiceError) => {
244
+ onError(err);
245
+ });
246
+ }
247
+ if (onStatus) {
248
+ call.on("status", (grpcStatus) => {
249
+ onStatus?.(new Status(grpcStatus.code, grpcStatus.details));
250
+ });
251
+ }
252
+
253
+ return {
254
+ call,
255
+ sendMetadata: (metadata: synapse.IPackageMetadata) => {
256
+ call.write({ metadata });
257
+ },
258
+ sendChunk: (fileChunk: Uint8Array) => {
259
+ call.write({ fileChunk });
260
+ },
261
+ end: () => {
262
+ call.end();
263
+ },
264
+ };
265
+ }
266
+
267
+ async deploy(
268
+ debPackagePath: string,
269
+ callbacks?: {
270
+ onProgress?: (bytesTransferred: number, totalBytes: number) => void;
271
+ onStatus?: (message: string) => void;
272
+ }
273
+ ): Promise<Status> {
274
+ const fileName = path.basename(debPackagePath);
275
+ const fileSize = fs.statSync(debPackagePath).size;
276
+ const sha256Sum = await calculateSha256(debPackagePath);
277
+ const version = extractVersion(fileName);
278
+
279
+ const metadata: synapse.IPackageMetadata = {
280
+ name: fileName,
281
+ version,
282
+ size: Long.fromNumber(fileSize),
283
+ sha256Sum,
284
+ };
285
+
286
+ return new Promise((resolve, reject) => {
287
+ let bytesTransferred = 0;
288
+
289
+ const { sendMetadata, sendChunk, end } = this.deployApp({}, {
290
+ onData: (response) => {
291
+ callbacks?.onStatus?.(response.message || "");
292
+ if (response.status !== synapse.StatusCode.kOk) {
293
+ reject(new Error(response.message || "Deployment failed"));
294
+ }
295
+ },
296
+ onEnd: () => {
297
+ resolve(new Status());
298
+ },
299
+ onError: (err) => {
300
+ reject(err);
301
+ },
302
+ });
303
+
304
+ sendMetadata(metadata);
305
+
306
+ const stream = fs.createReadStream(debPackagePath, {
307
+ highWaterMark: FILE_CHUNK_SIZE,
308
+ });
309
+
310
+ stream.on("data", (chunk: Buffer) => {
311
+ sendChunk(chunk);
312
+ bytesTransferred += chunk.length;
313
+ callbacks?.onProgress?.(bytesTransferred, fileSize);
314
+ });
315
+
316
+ stream.on("end", () => {
317
+ end();
318
+ });
319
+
320
+ stream.on("error", (err) => {
321
+ reject(err);
322
+ });
323
+ });
324
+ }
325
+
186
326
  _handleStatusResponse(status: synapse.IStatus): Status {
187
327
  const { code, message } = status;
188
328
  if (code !== synapse.StatusCode.kOk) {
@@ -53,3 +53,19 @@ message AppPerformanceSummary {
53
53
  uint64 timestamp_ns = 1;
54
54
  repeated FunctionProfile function_profiles = 2;
55
55
  }
56
+
57
+ // Information about an installed application
58
+ message AppInfo {
59
+ // Application name (e.g., "synapse-example-app")
60
+ string name = 1;
61
+
62
+ // Installed version
63
+ string version = 2;
64
+ }
65
+
66
+ message ListAppsRequest {
67
+ }
68
+
69
+ message ListAppsResponse {
70
+ repeated AppInfo apps = 1;
71
+ }
@@ -20,6 +20,7 @@ service SynapseDevice {
20
20
  rpc StreamQuery(StreamQueryRequest) returns (stream StreamQueryResponse) {}
21
21
 
22
22
  rpc DeployApp(stream AppPackageChunk) returns (stream AppDeployResponse) {}
23
+ rpc ListApps(ListAppsRequest) returns (ListAppsResponse) {}
23
24
 
24
25
  rpc ListFiles(google.protobuf.Empty) returns (ListFilesResponse) {}
25
26
  rpc WriteFile(WriteFileRequest) returns (WriteFileResponse) {}