hypha-rpc 0.1.0 → 0.1.1

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.
@@ -190,13 +190,12 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
190
190
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(client_id, "client_id is required");
191
191
  this._client_id = client_id;
192
192
  this._name = name;
193
- this._app_id = app_id;
193
+ this._app_id = app_id || "*";
194
194
  this._local_workspace = workspace;
195
195
  this.manager_id = manager_id;
196
196
  this._silent = silent;
197
197
  this.default_context = default_context || {};
198
198
  this._method_annotations = new WeakMap();
199
- this._manager_service = null;
200
199
  this._max_message_buffer_size = max_message_buffer_size;
201
200
  this._chunk_store = {};
202
201
  this._method_timeout = method_timeout || 30;
@@ -229,19 +228,21 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
229
228
  this._emit_message = connection.emit_message.bind(connection);
230
229
  connection.on_message(this._on_message.bind(this));
231
230
  this._connection = connection;
232
- connection.on_connect(async () => {
231
+ const updateServices = async () => {
233
232
  if (!this._silent && this.manager_id) {
234
233
  console.log("Connection established, reporting services...");
235
234
  for (let service of Object.values(this._services)) {
236
235
  const serviceInfo = this._extract_service_info(service);
237
236
  await this.emit({
238
237
  type: "service-added",
239
- to: this.manager_id,
238
+ to: "*/" + this.manager_id,
240
239
  service: serviceInfo,
241
240
  });
242
241
  }
243
242
  }
244
- });
243
+ };
244
+ connection.on_connect(updateServices);
245
+ updateServices();
245
246
  } else {
246
247
  this._emit_message = function () {
247
248
  console.log("No connection to emit message");
@@ -393,12 +394,12 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
393
394
  }
394
395
 
395
396
  async get_manager_service(timeout) {
396
- if (this.manager_id && !this._manager_service) {
397
- this._manager_service = await this.get_remote_service(
398
- `${this.manager_id}:default`,
399
- timeout,
400
- );
401
- }
397
+ (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(this.manager_id, "Manager id is not set");
398
+ const svc = await this.get_remote_service(
399
+ `*/${this.manager_id}:default`,
400
+ timeout,
401
+ );
402
+ return svc;
402
403
  }
403
404
 
404
405
  get_all_local_services() {
@@ -424,18 +425,18 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
424
425
  }
425
426
 
426
427
  // allow access for the same workspace
427
- if (context["from"].startsWith(ws + "/")) {
428
+ if (context["ws"] === ws) {
428
429
  return service;
429
430
  }
430
431
 
431
432
  throw new Error(
432
- `Permission denied for protected service: ${service_id}, workspace mismatch: ${ws} != ${context["from"]}`,
433
+ `Permission denied for getting protected service: ${service_id}, workspace mismatch: ${ws} != ${context["ws"]}`,
433
434
  );
434
435
  }
435
436
  async get_remote_service(service_uri, timeout) {
436
437
  timeout = timeout === undefined ? this._method_timeout : timeout;
437
438
  if (!service_uri && this.manager_id) {
438
- service_uri = this.manager_id;
439
+ service_uri = "*/" + this.manager_id;
439
440
  } else if (!service_uri.includes(":")) {
440
441
  service_uri = this._client_id + ":" + service_uri;
441
442
  }
@@ -444,7 +445,7 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
444
445
  if (service_id.includes("@")) {
445
446
  service_id = service_id.split("@")[0];
446
447
  const app_id = service_uri.split("@")[1];
447
- if (this._app_id)
448
+ if (this._app_id && this._app_id !== "*")
448
449
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(
449
450
  app_id === this._app_id,
450
451
  `Invalid app id: ${app_id} != ${this._app_id}`,
@@ -603,7 +604,7 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
603
604
  const [workspace, client_id] = context["to"].split("/");
604
605
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(client_id === this._client_id);
605
606
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(
606
- workspace === context["from"].split("/")[0],
607
+ workspace === context["ws"],
607
608
  "Services can only be registered from the same workspace",
608
609
  );
609
610
  }
@@ -611,13 +612,17 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
611
612
  const serviceInfo = this._extract_service_info(service);
612
613
  if (notify) {
613
614
  if (this.manager_id) {
614
- this.emit({
615
+ await this.emit({
615
616
  type: "service-added",
616
- to: this.manager_id,
617
+ to: "*/" + this.manager_id,
617
618
  service: serviceInfo,
618
619
  });
619
620
  } else {
620
- this.emit({ type: "service-added", to: "*", service: serviceInfo });
621
+ await this.emit({
622
+ type: "service-added",
623
+ to: "*",
624
+ service: serviceInfo,
625
+ });
621
626
  }
622
627
  }
623
628
  return serviceInfo;
@@ -636,7 +641,7 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
636
641
  if (this.manager_id) {
637
642
  this.emit({
638
643
  type: "service-removed",
639
- to: this.manager_id,
644
+ to: "*/" + this.manager_id,
640
645
  service: serviceInfo,
641
646
  });
642
647
  } else {
@@ -844,8 +849,6 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
844
849
  let from_client;
845
850
  if (!self._local_workspace) {
846
851
  from_client = self._client_id;
847
- } else if (self._local_workspace === "*") {
848
- from_client = target_id.split("/")[0] + "/" + self._client_id;
849
852
  } else {
850
853
  from_client = self._local_workspace + "/" + self._client_id;
851
854
  }
@@ -947,26 +950,6 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
947
950
  return remote_method;
948
951
  }
949
952
 
950
- async _notify_service_update() {
951
- if (this.manager_id) {
952
- // try to get the root service
953
- try {
954
- await this.get_manager_service(30.0);
955
- (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(this._manager_service);
956
- await this._manager_service.update_client_info(this.get_client_info());
957
- } catch (exp) {
958
- // pylint: disable=broad-except
959
- console.warn(
960
- "Failed to notify service update to",
961
- this.manager_id,
962
- exp,
963
- );
964
- }
965
- } else {
966
- console.warn("No manager id provided, cannot notify service update");
967
- }
968
- }
969
-
970
953
  get_client_info() {
971
954
  const services = [];
972
955
  for (let service of Object.values(this._services)) {
@@ -983,9 +966,10 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
983
966
  let reject = null;
984
967
  let heartbeat_task = null;
985
968
  try {
986
- (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(data["method"] && data["ctx"] && data["from"]);
969
+ (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(data.method && data.ctx && data.from);
987
970
  const method_name = data.from + ":" + data.method;
988
971
  const remote_workspace = data.from.split("/")[0];
972
+ const remote_client_id = data.from.split("/")[1];
989
973
  // Make sure the target id is an absolute id
990
974
  data["to"] = data["to"].includes("/")
991
975
  ? data["to"]
@@ -994,10 +978,8 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
994
978
  let local_workspace;
995
979
  if (!this._local_workspace) {
996
980
  local_workspace = data["to"].split("/")[0];
997
- } else if (this._local_workspace === "*") {
998
- local_workspace = remote_workspace;
999
981
  } else {
1000
- if (this._local_workspace) {
982
+ if (this._local_workspace && this._local_workspace !== "*") {
1001
983
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(
1002
984
  data["to"].split("/")[0] === this._local_workspace,
1003
985
  "Workspace mismatch: " +
@@ -1041,8 +1023,10 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
1041
1023
  try {
1042
1024
  method = indexObject(this._object_store, data["method"]);
1043
1025
  } catch (e) {
1044
- console.debug("Failed to find method", method_name, e);
1045
- throw new Error(`Method not found: ${method_name}`);
1026
+ console.debug("Failed to find method", method_name, this._client_id, e);
1027
+ throw new Error(
1028
+ `Method not found: ${method_name} at ${this._client_id}`,
1029
+ );
1046
1030
  }
1047
1031
 
1048
1032
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.assert)(
@@ -1054,9 +1038,12 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
1054
1038
  if (this._method_annotations.has(method)) {
1055
1039
  // For services, it should not be protected
1056
1040
  if (this._method_annotations.get(method).visibility === "protected") {
1057
- if (local_workspace !== remote_workspace) {
1041
+ if (
1042
+ local_workspace !== remote_workspace &&
1043
+ (remote_workspace !== "*" || remote_client_id !== this.manager_id)
1044
+ ) {
1058
1045
  throw new Error(
1059
- "Permission denied for protected method " +
1046
+ "Permission denied for invoking protected method " +
1060
1047
  method_name +
1061
1048
  ", workspace mismatch: " +
1062
1049
  local_workspace +
@@ -1076,7 +1063,7 @@ class RPC extends _utils_js__WEBPACK_IMPORTED_MODULE_0__.MessageEmitter {
1076
1063
  ) {
1077
1064
  session_target_id = local_workspace + "/" + session_target_id;
1078
1065
  }
1079
- if (this._local_workspace !== "*" && session_target_id !== data.from) {
1066
+ if (session_target_id !== data.from) {
1080
1067
  throw new Error(
1081
1068
  "Access denied for method call (" +
1082
1069
  method_name +
@@ -1993,10 +1980,6 @@ class WebRTCConnection {
1993
1980
  };
1994
1981
  }
1995
1982
 
1996
- set_reconnection_token(token) {
1997
- this._reconnection_token = token;
1998
- }
1999
-
2000
1983
  on_message(handler) {
2001
1984
  (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.assert)(handler, "handler is required");
2002
1985
  this._handle_message = handler;
@@ -2131,7 +2114,9 @@ async function getRTCService(server, service_id, config) {
2131
2114
  const rpc = await _setupRPC(config);
2132
2115
  pc.rpc = rpc;
2133
2116
  async function getService(name) {
2134
- return await rpc.get_remote_service(config.peer_id + ":" + name);
2117
+ return await rpc.get_remote_service(
2118
+ config.workspace + "/" + config.peer_id + ":" + name,
2119
+ );
2135
2120
  }
2136
2121
  async function disconnect() {
2137
2122
  await rpc.disconnect();
@@ -4073,16 +4058,6 @@ function utf8DecodeTD(bytes, inputOffset, byteLength) {
4073
4058
  }
4074
4059
  //# sourceMappingURL=utf8.mjs.map
4075
4060
 
4076
- /***/ }),
4077
-
4078
- /***/ "./package.json":
4079
- /*!**********************!*\
4080
- !*** ./package.json ***!
4081
- \**********************/
4082
- /***/ ((module) => {
4083
-
4084
- module.exports = /*#__PURE__*/JSON.parse('{"name":"hypha-rpc","version":"0.1.0","description":"Hypha RPC client for connecting to Hypha server for data management and AI model serving.","main":"index.js","types":"index.d.ts","scripts":{"build":"rm -rf dist && npm run build-umd","build-umd":"webpack --config webpack.config.js --mode development && NODE_ENV=production webpack --config webpack.config.js --mode production --devtool source-map","watch":"NODE_ENV=production webpack --watch --progress --config webpack.config.js --mode production --devtool source-map","publish-npm":"npm install && npm run build && npm publish","serve":"webpack serve","stats":"webpack --profile --json > stats.json","stats-prod":"webpack --profile --json --mode production > stats-prod.json","clean":"rimraf dist/*","format":"prettier --write \\"{src,tests}/**/**\\"","check-format":"prettier --check \\"{src,tests}/**/**\\"","test":"karma start --single-run --browsers ChromeHeadless,FirefoxHeadless karma.conf.js","test-watch":"karma start --auto-watch --browsers ChromeDebugging karma.conf.js --debug"},"repository":{"type":"git","url":"git+https://github.com/oeway/hypha-rpc.git"},"keywords":["hypha","rpc"],"author":"Wei Ouyang","license":"MIT","bugs":{"url":"https://github.com/oeway/hypha-rpc/issues"},"homepage":"https://github.com/oeway/hypha-rpc","engines":{"node":">=19.0.0"},"dependencies":{"@msgpack/msgpack":"^2.7.1"},"devDependencies":{"@babel/core":"^7.24.9","@babel/plugin-syntax-dynamic-import":"^7.8.3","@babel/preset-env":"^7.24.8","babel-loader":"^9.1.3","chai":"^5.1.1","clean-webpack-plugin":"^4.0.0","copy-webpack-plugin":"^12.0.2","eslint":"^7.32.0","eslint-config-prettier":"^9.1.0","eslint-loader":"^4.0.2","karma":"^6.4.3","karma-chrome-launcher":"^3.2.0","karma-firefox-launcher":"^2.1.3","karma-mocha":"^2.0.1","karma-sourcemap-loader":"^0.4.0","karma-spec-reporter":"^0.0.36","karma-webpack":"^5.0.1","mocha":"^10.6.0","prettier":"^3.3.3","webpack":"^5.93.0","webpack-cli":"^5.1.4","webpack-dev-server":"^5.0.4"}}');
4085
-
4086
4061
  /***/ })
4087
4062
 
4088
4063
  /******/ });
@@ -4149,7 +4124,6 @@ __webpack_require__.r(__webpack_exports__);
4149
4124
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4150
4125
  /* harmony export */ API_VERSION: () => (/* reexport safe */ _rpc_js__WEBPACK_IMPORTED_MODULE_0__.API_VERSION),
4151
4126
  /* harmony export */ RPC: () => (/* reexport safe */ _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC),
4152
- /* harmony export */ VERSION: () => (/* reexport safe */ _package_json__WEBPACK_IMPORTED_MODULE_3__.version),
4153
4127
  /* harmony export */ connectToServer: () => (/* binding */ connectToServer),
4154
4128
  /* harmony export */ getRTCService: () => (/* reexport safe */ _webrtc_client_js__WEBPACK_IMPORTED_MODULE_2__.getRTCService),
4155
4129
  /* harmony export */ loadRequirements: () => (/* reexport safe */ _utils_js__WEBPACK_IMPORTED_MODULE_1__.loadRequirements),
@@ -4160,8 +4134,6 @@ __webpack_require__.r(__webpack_exports__);
4160
4134
  /* harmony import */ var _rpc_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rpc.js */ "./src/rpc.js");
4161
4135
  /* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils.js */ "./src/utils.js");
4162
4136
  /* harmony import */ var _webrtc_client_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./webrtc-client.js */ "./src/webrtc-client.js");
4163
- /* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../package.json */ "./package.json");
4164
-
4165
4137
 
4166
4138
 
4167
4139
 
@@ -4193,7 +4165,6 @@ class WebsocketRPCConnection {
4193
4165
  this._disconnect_handler = null; // Disconnection event handler
4194
4166
  this._timeout = timeout * 1000; // Convert seconds to milliseconds
4195
4167
  this._WebSocketClass = WebSocketClass || WebSocket; // Allow overriding the WebSocket class
4196
- this._opening = null;
4197
4168
  this._closing = false;
4198
4169
  this._legacy_auth = null;
4199
4170
  this.connection_info = null;
@@ -4206,19 +4177,12 @@ class WebsocketRPCConnection {
4206
4177
 
4207
4178
  on_connect(handler) {
4208
4179
  this._handle_connect = handler;
4209
- if (this._websocket && this._websocket.readyState === WebSocket.OPEN) {
4210
- this._handle_connect(this);
4211
- }
4212
4180
  }
4213
4181
 
4214
4182
  on_disconnected(handler) {
4215
4183
  this._disconnect_handler = handler;
4216
4184
  }
4217
4185
 
4218
- set_reconnection_token(token) {
4219
- this._reconnection_token = token;
4220
- }
4221
-
4222
4186
  async _attempt_connection(server_url, attempt_fallback = true) {
4223
4187
  return new Promise((resolve, reject) => {
4224
4188
  this._legacy_auth = false;
@@ -4282,7 +4246,6 @@ class WebsocketRPCConnection {
4282
4246
  return; // Avoid opening a new connection if closing or already open
4283
4247
  }
4284
4248
  try {
4285
- this._opening = true;
4286
4249
  this._websocket = await this._attempt_connection(this._server_url);
4287
4250
  if (!this._legacy_auth) {
4288
4251
  // Send authentication info as the first message if connected without query params
@@ -4302,13 +4265,17 @@ class WebsocketRPCConnection {
4302
4265
  if (!first_message.success) {
4303
4266
  const error = first_message.error || "Unknown error";
4304
4267
  console.error("Failed to connect, " + error);
4305
- this.connection_info = None;
4268
+ this.connection_info = null;
4306
4269
  reject(new Error(error));
4307
4270
  } else if (first_message) {
4308
4271
  console.log(
4309
4272
  "Successfully connected: " + JSON.stringify(first_message),
4310
4273
  );
4311
4274
  this.connection_info = first_message;
4275
+ if (this.connection_info.reconnection_token) {
4276
+ this._reconnection_token =
4277
+ this.connection_info.reconnection_token;
4278
+ }
4312
4279
  }
4313
4280
  resolve();
4314
4281
  };
@@ -4319,8 +4286,7 @@ class WebsocketRPCConnection {
4319
4286
  }
4320
4287
 
4321
4288
  this._websocket.onmessage = (event) => {
4322
- const data = event.data;
4323
- this._handle_message(data);
4289
+ this._handle_message(event.data);
4324
4290
  };
4325
4291
 
4326
4292
  if (this._handle_connect) {
@@ -4328,8 +4294,6 @@ class WebsocketRPCConnection {
4328
4294
  }
4329
4295
  } catch (error) {
4330
4296
  console.error("Failed to connect to", this._server_url, error);
4331
- } finally {
4332
- this._opening = false;
4333
4297
  }
4334
4298
  }
4335
4299
 
@@ -4337,9 +4301,8 @@ class WebsocketRPCConnection {
4337
4301
  if (this._closing) {
4338
4302
  throw new Error("Connection is closing");
4339
4303
  }
4340
- await this._opening;
4341
4304
  if (!this._websocket || this._websocket.readyState !== WebSocket.OPEN) {
4342
- throw new Error("WebSocket connection is not open");
4305
+ await this.open();
4343
4306
  }
4344
4307
  try {
4345
4308
  this._websocket.send(data);
@@ -4418,27 +4381,33 @@ async function connectToServer(config) {
4418
4381
  config.WebSocketClass,
4419
4382
  );
4420
4383
  await connection.open();
4421
- let workspace = config.workspace;
4422
- if (connection.connection_info) {
4423
- workspace = connection.connection_info.workspace;
4384
+ (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__.assert)(connection.connection_info, "Failed to connect to the server");
4385
+ if (
4386
+ config.workspace &&
4387
+ connection.connection_info.workspace !== config.workspace
4388
+ ) {
4389
+ throw new Error(
4390
+ `Connected to the wrong workspace: ${connection.connection_info.workspace}, expected: ${config.workspace}`,
4391
+ );
4424
4392
  }
4393
+ const workspace = connection.connection_info.workspace;
4394
+ const manager_id = connection.connection_info.manager_id;
4425
4395
  const rpc = new _rpc_js__WEBPACK_IMPORTED_MODULE_0__.RPC(connection, {
4426
4396
  client_id: clientId,
4427
4397
  workspace,
4428
- manager_id: "workspace-manager",
4398
+ manager_id,
4429
4399
  default_context: { connection_type: "websocket" },
4430
4400
  name: config.name,
4431
4401
  method_timeout: config.method_timeout,
4432
4402
  app_id: config.app_id,
4433
4403
  });
4434
- const wm = await rpc.get_remote_service("workspace-manager:default");
4404
+ const wm = await rpc.get_manager_service();
4435
4405
  wm.rpc = rpc;
4436
4406
 
4437
4407
  async function _export(api) {
4438
4408
  api.id = "default";
4439
4409
  api.name = api.name || config.name || api.id;
4440
4410
  api.description = api.description || config.description;
4441
- api.docs = api.docs || config.docs;
4442
4411
  await rpc.register_service(api, true);
4443
4412
  }
4444
4413
 
@@ -4450,8 +4419,9 @@ async function connectToServer(config) {
4450
4419
  await rpc.disconnect();
4451
4420
  await connection.disconnect();
4452
4421
  }
4453
-
4454
- wm.config["client_id"] = clientId;
4422
+ if (connection.connection_info) {
4423
+ wm.config = Object.assign(wm.config, connection.connection_info);
4424
+ }
4455
4425
  wm.export = _export;
4456
4426
  wm.getPlugin = getPlugin;
4457
4427
  wm.listPlugins = wm.listServices;
@@ -4461,7 +4431,7 @@ async function connectToServer(config) {
4461
4431
  wm.on = rpc.on;
4462
4432
  if (rpc.manager_id) {
4463
4433
  rpc.on("force-exit", async (message) => {
4464
- if (message.from.endsWith("/" + rpc.manager_id)) {
4434
+ if (message.from === "*/" + rpc.manager_id) {
4465
4435
  console.log("Disconnecting from server, reason:", message.reason);
4466
4436
  await disconnect();
4467
4437
  }
@@ -4714,11 +4684,6 @@ function setupLocalClient({
4714
4684
  }
4715
4685
  resolve(server);
4716
4686
  } catch (e) {
4717
- // If any script fails to load, send an error message
4718
- await server.update_client_info({
4719
- id: client_id,
4720
- error: e.message,
4721
- });
4722
4687
  reject(e);
4723
4688
  }
4724
4689
  });