@holochain/hc-spin 0.200.4 → 0.200.5

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.
@@ -7,11 +7,11 @@ const commander = require("commander");
7
7
  const contextMenu = require("electron-context-menu");
8
8
  const split = require("split");
9
9
  const childProcess = require("child_process");
10
- const msgpack = require("@msgpack/msgpack");
11
10
  const url = require("url");
12
11
  const utils = require("@electron-toolkit/utils");
13
12
  const net$1 = require("node:net");
14
13
  const os = require("node:os");
14
+ const msgpack = require("@msgpack/msgpack");
15
15
  const require$$0$3 = require("events");
16
16
  const require$$1$1 = require("https");
17
17
  const require$$2$1 = require("http");
@@ -11735,25 +11735,34 @@ function validateCliArgs(cliArgs, cliOpts, appDataRootDir) {
11735
11735
  appId,
11736
11736
  holochainPath,
11737
11737
  numAgents,
11738
+ networkSeed: cliOpts.networkSeed,
11738
11739
  uiSource: cliOpts.uiPath ? { type: "path", path: cliOpts.uiPath } : cliOpts.uiPort ? { type: "port", port: cliOpts.uiPort } : { type: "path", path: path.join(appDataRootDir, "apps", appId, "ui") },
11740
+ singalingUrl: cliOpts.signalingUrl,
11741
+ bootstrapUrl: cliOpts.bootstrapUrl,
11739
11742
  happOrWebhappPath: isHapp ? { type: "happ", path: happOrWebhappPath } : { type: "webhapp", path: happOrWebhappPath }
11740
11743
  };
11741
11744
  }
11742
11745
  const rustUtils = require("@holochain/hc-spin-rust-utils");
11743
11746
  const cli = new commander.Command();
11744
- cli.name("hc-spin").description("CLI to run Holochain apps during development.").version(`0.100.0 (for holochain 0.1.x)`).argument(
11747
+ cli.name("hc-spin").description("CLI to run Holochain aps during development.").version(`0.200.4 (for holochain 0.2.x)`).argument(
11745
11748
  "<path>",
11746
11749
  "Path to .webhapp or .happ file to launch. If a .happ file is passed, either a UI path must be specified via --ui-path or a port pointing to a localhost server via --ui-port"
11747
11750
  ).option(
11748
11751
  "--app-id <string>",
11749
11752
  "Install the app with a specific app id. By default the app id is derived from the name of the .webhapp/.happ file that you pass but this option allows you to set it explicitly"
11753
+ ).option(
11754
+ "--bootstrap-url <url>",
11755
+ "Url of the bootstrap server to use. By default, hc spin spins up a local development bootstrap server for you but this argument allows you to specify a custom one."
11750
11756
  ).option("--holochain-path <path>", "Set the path to the holochain binary [default: holochain].").addOption(
11751
11757
  new commander.Option("-n, --num-agents <number>", "How many agents to spawn the app for.").argParser(
11752
11758
  parseInt
11753
11759
  )
11754
- ).option("--ui-path <path>", "Path to the folder containing the index.html of the webhapp's UI.").option(
11760
+ ).option("--network-seed <string>", "Install the app with a specific network seed.").option("--ui-path <path>", "Path to the folder containing the index.html of the webhapp's UI.").option(
11755
11761
  "--ui-port <number>",
11756
11762
  "Port pointing to a localhost dev server that serves your UI assets."
11763
+ ).option(
11764
+ "--signaling-url <url>",
11765
+ "Url of the signaling server to use. By default, hc spin spins up a local development signaling server for you but this argument allows you to specify a custom one."
11757
11766
  );
11758
11767
  cli.parse();
11759
11768
  const rl = require("readline").createInterface({
@@ -11827,7 +11836,36 @@ const handleSignZomeCallLegacy = async (e, request) => {
11827
11836
  return Promise.reject("Agent public key unauthorized.");
11828
11837
  return windowInfo.zomeCallSigner.signZomeCall(request);
11829
11838
  };
11830
- async function spawnSandboxes(nAgents, happPath, appId) {
11839
+ async function startLocalServices() {
11840
+ const localServicesHandle = childProcess__namespace.spawn("hc", ["run-local-services"]);
11841
+ return new Promise((resolve) => {
11842
+ let bootStrapUrl;
11843
+ let signalUrl;
11844
+ let bootstrapRunning = false;
11845
+ let signalRunnig = false;
11846
+ localServicesHandle.stdout.pipe(split()).on("data", async (line) => {
11847
+ console.log(`[hc-spin] | [hc run-local-services]: ${line}`);
11848
+ if (line.includes("HC BOOTSTRAP - ADDR:")) {
11849
+ bootStrapUrl = line.split("# HC BOOTSTRAP - ADDR:")[1].trim();
11850
+ }
11851
+ if (line.includes("HC SIGNAL - ADDR:")) {
11852
+ signalUrl = line.split("# HC SIGNAL - ADDR:")[1].trim();
11853
+ }
11854
+ if (line.includes("HC BOOTSTRAP - RUNNING")) {
11855
+ bootstrapRunning = true;
11856
+ }
11857
+ if (line.includes("HC SIGNAL - RUNNING")) {
11858
+ signalRunnig = true;
11859
+ }
11860
+ if (bootstrapRunning && signalRunnig)
11861
+ resolve([bootStrapUrl, signalUrl]);
11862
+ });
11863
+ localServicesHandle.stderr.pipe(split()).on("data", async (line) => {
11864
+ console.log(`[hc-spin] | [hc run-local-services] ERROR: ${line}`);
11865
+ });
11866
+ });
11867
+ }
11868
+ async function spawnSandboxes(nAgents, happPath, bootStrapUrl, signalUrl, appId, networkSeed) {
11831
11869
  const generateArgs = [
11832
11870
  "sandbox",
11833
11871
  "--piped",
@@ -11835,18 +11873,22 @@ async function spawnSandboxes(nAgents, happPath, appId) {
11835
11873
  "--num-sandboxes",
11836
11874
  nAgents.toString(),
11837
11875
  "--app-id",
11838
- appId
11876
+ appId,
11877
+ "--run"
11839
11878
  ];
11840
- const appPorts = [];
11841
- let appPortsString = "";
11879
+ let appPorts = "";
11842
11880
  for (var i = 1; i <= nAgents; i++) {
11843
11881
  const appPort = await getPorts();
11844
- appPortsString += `${appPort},`;
11845
- appPorts.push(appPort);
11882
+ appPorts += `${appPort},`;
11883
+ }
11884
+ generateArgs.push(appPorts.slice(0, appPorts.length - 1));
11885
+ if (networkSeed) {
11886
+ generateArgs.push("--network-seed");
11887
+ generateArgs.push(networkSeed);
11846
11888
  }
11847
- generateArgs.push("--run", appPortsString.slice(0, appPortsString.length - 1));
11848
- generateArgs.push(happPath, "network", "mdns");
11889
+ generateArgs.push(happPath, "network", "--bootstrap", bootStrapUrl, "webrtc", signalUrl);
11849
11890
  let readyConductors = 0;
11891
+ const portsInfo = {};
11850
11892
  const sandboxPaths = [];
11851
11893
  const sandboxHandle = childProcess__namespace.spawn("hc", generateArgs);
11852
11894
  sandboxHandle.stdin.write("pass");
@@ -11858,10 +11900,17 @@ async function spawnSandboxes(nAgents, happPath, appId) {
11858
11900
  const sanboxPath = line.split("\x1B[1;4;48;5;254;38;5;4m")[1].split("\x1B[0m \x1B[1m")[0].trim();
11859
11901
  sandboxPaths.push(sanboxPath);
11860
11902
  }
11861
- if (line.includes("Running conductor on admin port")) {
11903
+ if (line.includes("lair-keystore connection_url")) {
11904
+ line.split("#")[2].trim();
11905
+ }
11906
+ if (line.includes("Conductor launched")) {
11907
+ const split1 = line.split("{");
11908
+ const ports = JSON.parse(`{${split1[1]}`);
11909
+ const conductorNum = split1[0].split("#!")[1].trim();
11910
+ portsInfo[conductorNum] = ports;
11862
11911
  readyConductors += 1;
11863
11912
  if (readyConductors === nAgents)
11864
- resolve([sandboxHandle, sandboxPaths, appPorts]);
11913
+ resolve([sandboxHandle, sandboxPaths, portsInfo]);
11865
11914
  }
11866
11915
  });
11867
11916
  sandboxHandle.stderr.pipe(split()).on("data", async (line) => {
@@ -11883,12 +11932,14 @@ electron.app.whenReady().then(async () => {
11883
11932
  happTargetDir
11884
11933
  );
11885
11934
  }
11886
- const [sandboxHandle, sandboxPaths, appPorts] = await spawnSandboxes(
11935
+ const [bootstrapUrl, signalingUrl] = await startLocalServices();
11936
+ const [sandboxHandle, sandboxPaths, portsInfo] = await spawnSandboxes(
11887
11937
  CLI_OPTS.numAgents,
11888
11938
  happTargetDir ? happTargetDir : CLI_OPTS.happOrWebhappPath.path,
11939
+ CLI_OPTS.bootstrapUrl ? CLI_OPTS.bootstrapUrl : bootstrapUrl,
11940
+ CLI_OPTS.singalingUrl ? CLI_OPTS.singalingUrl : signalingUrl,
11889
11941
  CLI_OPTS.appId
11890
11942
  );
11891
- console.log("Got app ports: ", appPorts);
11892
11943
  const lairUrls = [];
11893
11944
  sandboxPaths.forEach((sandbox) => {
11894
11945
  const conductorConfigPath = path.join(sandbox, "conductor-config.yaml");
@@ -11905,14 +11956,15 @@ electron.app.whenReady().then(async () => {
11905
11956
  SANDBOX_PROCESSES.push(sandboxHandle);
11906
11957
  for (var i = 0; i < cli.opts().numAgents; i++) {
11907
11958
  const zomeCallSigner = await rustUtils.ZomeCallSigner.connect(lairUrls[i], "pass");
11908
- const appWs = await AppWebsocket.connect(new URL(`ws://127.0.0.1:${appPorts[i]}`));
11959
+ const appPort = portsInfo[i].app_ports[0];
11960
+ const appWs = await AppWebsocket.connect(new URL(`ws://127.0.0.1:${appPort}`));
11909
11961
  const appInfo = await appWs.appInfo({ installed_app_id: CLI_OPTS.appId });
11910
11962
  const happWindow = await createHappWindow(
11911
11963
  CLI_OPTS.uiSource,
11912
11964
  CLI_OPTS.happOrWebhappPath,
11913
11965
  CLI_OPTS.appId,
11914
11966
  i + 1,
11915
- appPorts[i],
11967
+ appPort,
11916
11968
  DATA_ROOT_DIR
11917
11969
  );
11918
11970
  WINDOW_INFO_MAP[happWindow.webContents.id] = {
@@ -11922,7 +11974,9 @@ electron.app.whenReady().then(async () => {
11922
11974
  }
11923
11975
  });
11924
11976
  electron.app.on("window-all-closed", () => {
11925
- electron.app.quit();
11977
+ if (process.platform !== "darwin") {
11978
+ electron.app.quit();
11979
+ }
11926
11980
  });
11927
11981
  electron.app.on("quit", () => {
11928
11982
  fs.writeFileSync(
package/package.json CHANGED
@@ -1,9 +1,21 @@
1
1
  {
2
2
  "name": "@holochain/hc-spin",
3
- "version": "0.200.4",
3
+ "version": "0.200.5",
4
4
  "description": "CLI to run Holochain aps during development.",
5
5
  "author": "matthme",
6
6
  "homepage": "https://developer.holochain.org",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/holochain/hc-spin.git"
10
+ },
11
+ "keywords": [
12
+ "holochain",
13
+ "developer",
14
+ "tools",
15
+ "tooling",
16
+ "developer-tools",
17
+ "cli"
18
+ ],
7
19
  "license": "MIT",
8
20
  "main": "out/main/index.js",
9
21
  "bin": {
@@ -23,12 +35,12 @@
23
35
  "@electron-toolkit/preload": "^3.0.0",
24
36
  "@electron-toolkit/utils": "^3.0.0",
25
37
  "@holochain/client": "^0.16.3",
38
+ "@holochain/hc-spin-rust-utils": "0.200.2",
39
+ "@msgpack/msgpack": "^2.8.0",
26
40
  "commander": "11.1.0",
27
41
  "electron": "^28.1.1",
28
42
  "electron-context-menu": "3.6.1",
29
43
  "get-port": "7.0.0",
30
- "@holochain/hc-spin-rust-utils": "0.200.2",
31
- "@msgpack/msgpack": "^2.8.0",
32
44
  "nanoid": "5.0.4",
33
45
  "split": "1.0.1"
34
46
  },
package/src/main/index.ts CHANGED
@@ -61,6 +61,21 @@ cli.parse();
61
61
  // console.log('Got CLI opts: ', cli.opts());
62
62
  // console.log('Got CLI args: ', cli.args);
63
63
 
64
+ // In nix shell and on Windows SIGINT does not seem to be emitted so it is read from the command line instead.
65
+ // https://stackoverflow.com/questions/10021373/what-is-the-windows-equivalent-of-process-onsigint-in-node-js
66
+ const rl = require('readline').createInterface({
67
+ input: process.stdin,
68
+ output: process.stdout,
69
+ });
70
+
71
+ rl.on('SIGINT', function () {
72
+ process.emit('SIGINT');
73
+ });
74
+
75
+ process.on('SIGINT', () => {
76
+ app.quit();
77
+ });
78
+
64
79
  // Garbage collect unused directories of previous runs
65
80
  const files = fs.readdirSync(app.getPath('temp'));
66
81
  const hcSpinFolders = files.filter((file) => file.startsWith(`hc-spin-`));