@sharpe-jupyter/connect 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +75 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import Spinner from "ink-spinner";
10
10
  import { spawn as spawn2 } from "child_process";
11
11
 
12
12
  // src/cloudflared.ts
13
- import { createWriteStream, existsSync, mkdirSync, chmodSync } from "fs";
13
+ import { createWriteStream, existsSync, mkdirSync, chmodSync, unlinkSync } from "fs";
14
14
  import { join } from "path";
15
15
  import { homedir, platform, arch } from "os";
16
16
  import { pipeline } from "stream/promises";
@@ -61,7 +61,7 @@ async function ensureCloudflared() {
61
61
  fileStream
62
62
  );
63
63
  execSync(`tar -xzf "${tgzPath}" -C "${binDir}" cloudflared`, { stdio: "ignore" });
64
- execSync(`rm -f "${tgzPath}"`, { stdio: "ignore" });
64
+ unlinkSync(tgzPath);
65
65
  } else {
66
66
  const fileStream = createWriteStream(binPath);
67
67
  await pipeline(
@@ -75,6 +75,53 @@ async function ensureCloudflared() {
75
75
  return { path: binPath, downloaded: true };
76
76
  }
77
77
 
78
+ // src/ripgrep.ts
79
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync as chmodSync2, unlinkSync as unlinkSync2, createWriteStream as createWriteStream2 } from "fs";
80
+ import { join as join2 } from "path";
81
+ import { homedir as homedir2, arch as arch2 } from "os";
82
+ import { pipeline as pipeline2 } from "stream/promises";
83
+ import { Readable as Readable2 } from "stream";
84
+ import { execSync as execSync2 } from "child_process";
85
+ var RIPGREP_VERSION = "15.1.0";
86
+ function getAssetName() {
87
+ const cpu = arch2();
88
+ const triple = cpu === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin";
89
+ return `ripgrep-${RIPGREP_VERSION}-${triple}.tar.gz`;
90
+ }
91
+ function getBinDir2() {
92
+ return join2(homedir2(), ".sharpe", "bin");
93
+ }
94
+ function getRipgrepPath() {
95
+ return join2(getBinDir2(), "rg");
96
+ }
97
+ async function ensureRipgrep() {
98
+ const binPath = getRipgrepPath();
99
+ if (existsSync2(binPath)) {
100
+ return { path: binPath, downloaded: false };
101
+ }
102
+ const binDir = getBinDir2();
103
+ mkdirSync2(binDir, { recursive: true });
104
+ const assetName = getAssetName();
105
+ const url = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/${assetName}`;
106
+ const response = await fetch(url, { redirect: "follow" });
107
+ if (!response.ok || !response.body) {
108
+ throw new Error(`Failed to download ripgrep: ${response.status} ${response.statusText}`);
109
+ }
110
+ const tgzPath = join2(binDir, assetName);
111
+ const fileStream = createWriteStream2(tgzPath);
112
+ await pipeline2(
113
+ Readable2.fromWeb(response.body),
114
+ fileStream
115
+ );
116
+ const dirName = assetName.replace(".tar.gz", "");
117
+ execSync2(`tar -xzf "${tgzPath}" -C "${binDir}" "${dirName}/rg"`, { stdio: "ignore" });
118
+ execSync2(`mv "${join2(binDir, dirName, "rg")}" "${binPath}"`, { stdio: "ignore" });
119
+ unlinkSync2(tgzPath);
120
+ execSync2(`rm -rf "${join2(binDir, dirName)}"`, { stdio: "ignore" });
121
+ chmodSync2(binPath, 493);
122
+ return { path: binPath, downloaded: true };
123
+ }
124
+
78
125
  // src/health.ts
79
126
  async function checkJupyterHealth(port2) {
80
127
  try {
@@ -88,20 +135,22 @@ async function checkJupyterHealth(port2) {
88
135
  }
89
136
 
90
137
  // src/jupyter.ts
91
- import { execSync as execSync2, spawn } from "child_process";
92
- import { existsSync as existsSync2 } from "fs";
93
- import { join as join2 } from "path";
94
- import { homedir as homedir2, platform as platform2 } from "os";
95
- var VENV_DIR = join2(homedir2(), ".sharpe", "venv");
138
+ import { execSync as execSync3, spawn } from "child_process";
139
+ import { existsSync as existsSync3 } from "fs";
140
+ import { join as join3 } from "path";
141
+ import { homedir as homedir3, platform as platform2 } from "os";
142
+ var SHARPE_DIR = join3(homedir3(), ".sharpe");
143
+ var VENV_DIR = join3(SHARPE_DIR, "venv");
144
+ var SHARPE_BIN_DIR = join3(SHARPE_DIR, "bin");
96
145
  var IS_WIN = platform2() === "win32";
97
146
  var BIN_DIR = IS_WIN ? "Scripts" : "bin";
98
147
  function venvBin(name) {
99
- return join2(VENV_DIR, BIN_DIR, IS_WIN ? `${name}.exe` : name);
148
+ return join3(VENV_DIR, BIN_DIR, IS_WIN ? `${name}.exe` : name);
100
149
  }
101
150
  function findPython() {
102
151
  for (const cmd of ["python3", "python"]) {
103
152
  try {
104
- const version = execSync2(`${cmd} --version`, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
153
+ const version = execSync3(`${cmd} --version`, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
105
154
  if (version.startsWith("Python 3")) return cmd;
106
155
  } catch {
107
156
  }
@@ -109,10 +158,10 @@ function findPython() {
109
158
  return null;
110
159
  }
111
160
  function venvExists() {
112
- return existsSync2(venvBin("python"));
161
+ return existsSync3(venvBin("python"));
113
162
  }
114
163
  function createVenv(pythonCmd) {
115
- execSync2(`${pythonCmd} -m venv "${VENV_DIR}"`, { stdio: "ignore" });
164
+ execSync3(`${pythonCmd} -m venv "${VENV_DIR}"`, { stdio: "ignore" });
116
165
  }
117
166
  function installPackages(packages) {
118
167
  return spawn(venvBin("pip"), ["install", "--quiet", ...packages], {
@@ -120,6 +169,7 @@ function installPackages(packages) {
120
169
  });
121
170
  }
122
171
  function startJupyter(port2) {
172
+ const env = { ...process.env, PATH: `${SHARPE_BIN_DIR}:${process.env.PATH ?? ""}` };
123
173
  return spawn(
124
174
  venvBin("jupyter"),
125
175
  [
@@ -130,9 +180,10 @@ function startJupyter(port2) {
130
180
  "--ServerApp.token=",
131
181
  "--ServerApp.password=",
132
182
  "--ServerApp.allow_remote_access=True",
133
- "--ServerApp.disable_check_xsrf=True"
183
+ "--ServerApp.disable_check_xsrf=True",
184
+ '--ServerApp.jpserver_extensions={"sharpe_log_handler.handler": true}'
134
185
  ],
135
- { stdio: ["ignore", "pipe", "pipe"] }
186
+ { stdio: ["ignore", "pipe", "pipe"], env }
136
187
  );
137
188
  }
138
189
  function parseJupyterLine(line) {
@@ -321,12 +372,12 @@ function App({ connectionCode: connectionCode2, port: port2 }) {
321
372
  let cancelled = false;
322
373
  async function run() {
323
374
  try {
324
- await ensureCloudflared();
375
+ await Promise.all([ensureCloudflared(), ensureRipgrep()]);
325
376
  } catch {
326
377
  if (cancelled) return;
327
378
  setSetupStep("error");
328
379
  setError({
329
- message: "Could not set up the connection tool.",
380
+ message: "Could not set up required tools.",
330
381
  hint: "Check your internet connection and try again."
331
382
  });
332
383
  return;
@@ -368,7 +419,7 @@ function App({ connectionCode: connectionCode2, port: port2 }) {
368
419
  if (cancelled) return;
369
420
  pushEvent("Installing Jupyter...");
370
421
  const installResult = await new Promise((resolve) => {
371
- const proc2 = installPackages(["jupyter", "jupyter-resource-usage"]);
422
+ const proc2 = installPackages(["jupyter", "jupyter-resource-usage", "sharpe-log-handler"]);
372
423
  proc2.on("error", (err) => resolve(err.message));
373
424
  proc2.on("exit", (code) => resolve(code === 0 ? null : "install-failed"));
374
425
  });
@@ -608,7 +659,14 @@ Close whatever is using it and try again, or use a different port:
608
659
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
609
660
  setupStep === "done" && /* @__PURE__ */ jsx(Step, { status: "done", label: "Setting up" }),
610
661
  setupStep === "error" && /* @__PURE__ */ jsx(Step, { status: "error", label: "Setting up" }),
611
- jupyterStep === "done" && /* @__PURE__ */ jsx(Step, { status: "done", label: "Notebook server running", detail: `http://localhost:${port2}` }),
662
+ jupyterStep === "done" && /* @__PURE__ */ jsx(
663
+ Step,
664
+ {
665
+ status: "done",
666
+ label: "Notebook server running",
667
+ detail: `http://localhost:${port2}`
668
+ }
669
+ ),
612
670
  jupyterStep === "error" && /* @__PURE__ */ jsx(Step, { status: "error", label: "Could not start notebook server" }),
613
671
  tunnelStep === "error" && /* @__PURE__ */ jsx(Step, { status: "error", label: "Connecting to Sharpe" })
614
672
  ] }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sharpe-jupyter/connect",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Connect a local JupyterHub to Sharpe via Cloudflare Tunnel",
5
5
  "type": "module",
6
6
  "bin": {