shardstitch 0.0.3 → 0.0.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/cli.js +220 -43
  3. package/package.json +27 -6
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ShardStitch
2
2
 
3
- **3:47pm. Mid-refactor. "Rate limit reached."**
3
+ **"Rate limit reached." "Context window full." "VRAM out of memory."**
4
4
 
5
5
  Your code is half-written. Your plan is in your head. ShardStitch captures your entire session — git diff, changed files, dependency graph, intent — and stitches it into the next AI tool. One hotkey. Keep building like nothing happened.
6
6
 
package/cli.js CHANGED
@@ -1,41 +1,43 @@
1
1
  #!/usr/bin/env node
2
- // ShardStitch zero-friction launcher: `npx shardstitch`
3
- // Finds a local install (exe or Python source) and starts the dashboard.
4
- // Falls back to download instructions if nothing is installed.
2
+ // ShardStitch launcher `shardstitch` or `shardstitch install <key>`
3
+ // Finds a local install and starts the dashboard.
4
+ // Run `shardstitch install <key>` after purchase to download the app.
5
5
 
6
6
  const { spawnSync, spawn } = require("child_process");
7
- const { existsSync } = require("fs");
8
- const { join } = require("path");
7
+ const { existsSync, mkdirSync, createWriteStream, chmodSync, renameSync } = require("fs");
8
+ const { join, dirname } = require("path");
9
+ const { createHash } = require("crypto");
9
10
  const os = require("os");
11
+ const https = require("https");
12
+ const fs = require("fs");
13
+
14
+ const POLAR_ORG_ID = "8e901881-4e3d-4356-98fb-2511337101b1";
15
+ const INSTALL_DIR = join(os.homedir(), ".shardstitch", "app");
16
+ const ACTIVATION_FILE = join(os.homedir(), ".shardstitch", "activation.json");
17
+ const MACHINE_SALT = "shardstitch-activation/1";
18
+
19
+ const PLATFORM = process.platform === "win32" ? "windows"
20
+ : process.platform === "darwin" ? "mac"
21
+ : "linux";
22
+
23
+ const EXE_NAME = PLATFORM === "windows" ? "ShardStitch.exe" : "shardstitch";
10
24
 
11
25
  const CANDIDATE_DIRS = [
12
26
  process.env.SHARDSTITCH_HOME,
27
+ INSTALL_DIR,
13
28
  join(os.homedir(), "ShardStitch"),
14
- join(os.homedir(), ".shardstitch", "app"),
15
- process.platform === "win32" ? join(process.env.LOCALAPPDATA || "", "ShardStitch") : null,
16
- process.platform === "win32" ? "C:\\Program Files\\ShardStitch" : "/opt/shardstitch",
29
+ PLATFORM === "windows" ? join(process.env.LOCALAPPDATA || "", "ShardStitch") : null,
30
+ PLATFORM === "windows" ? "C:\\Program Files\\ShardStitch" : "/opt/shardstitch",
17
31
  ].filter(Boolean);
18
32
 
19
33
  function findExe() {
20
- const exeName = process.platform === "win32" ? "ShardStitch.exe" : "shardstitch";
21
34
  for (const dir of CANDIDATE_DIRS) {
22
- const p = join(dir, exeName);
35
+ const p = join(dir, EXE_NAME);
23
36
  if (existsSync(p)) return p;
24
37
  }
25
38
  return null;
26
39
  }
27
40
 
28
- function findPythonApp() {
29
- for (const dir of CANDIDATE_DIRS) {
30
- const p = join(dir, "app.py");
31
- if (existsSync(p)) return p;
32
- }
33
- // Also: running from a source checkout
34
- const local = join(process.cwd(), "app.py");
35
- if (existsSync(local)) return local;
36
- return null;
37
- }
38
-
39
41
  function pythonCmd() {
40
42
  for (const cmd of ["python", "python3", "py"]) {
41
43
  const r = spawnSync(cmd, ["--version"], { stdio: "ignore", shell: false });
@@ -44,29 +46,204 @@ function pythonCmd() {
44
46
  return null;
45
47
  }
46
48
 
47
- const exe = findExe();
48
- if (exe) {
49
- console.log(" Starting ShardStitch from " + exe);
50
- spawn(exe, [], { detached: true, stdio: "ignore" }).unref();
51
- console.log(" Dashboard: http://127.0.0.1:8765");
52
- process.exit(0);
49
+ function machineFingerprint() {
50
+ const raw = `${MACHINE_SALT}|${process.env.COMPUTERNAME || ""}|${process.env.USERNAME || os.userInfo().username}`;
51
+ return createHash("sha256").update(raw).digest("hex").slice(0, 16);
52
+ }
53
+
54
+ function activationSignature(key, store) {
55
+ const raw = `${MACHINE_SALT}|${key}|${store}|${machineFingerprint()}`;
56
+ return createHash("sha256").update(raw).digest("hex");
53
57
  }
54
58
 
55
- const appPy = findPythonApp();
56
- const py = pythonCmd();
57
- if (appPy && py) {
58
- console.log(" Starting ShardStitch (Python) from " + appPy);
59
- spawn(py, [appPy], { detached: true, stdio: "ignore", cwd: join(appPy, "..") }).unref();
60
- console.log(" Dashboard: http://127.0.0.1:8765");
61
- process.exit(0);
59
+ function saveActivation(key) {
60
+ mkdirSync(dirname(ACTIVATION_FILE), { recursive: true });
61
+ const record = {
62
+ key,
63
+ store: "polar",
64
+ activatedAt: Math.floor(Date.now() / 1000),
65
+ signature: activationSignature(key, "polar"),
66
+ };
67
+ fs.writeFileSync(ACTIVATION_FILE, JSON.stringify(record, null, 2));
62
68
  }
63
69
 
64
- console.log("");
65
- console.log(" ShardStitch — move your AI coding session between tools.");
66
- console.log("");
67
- console.log(" No local install found. Get it at: https://shardstitch.com");
68
- console.log(" (one-time purchase, runs 100% locally, zero cloud)");
69
- console.log("");
70
- console.log(" After installing, run `npx shardstitch` again to launch.");
71
- console.log("");
72
- process.exit(1);
70
+ function isActivated() {
71
+ try {
72
+ const data = JSON.parse(fs.readFileSync(ACTIVATION_FILE, "utf8"));
73
+ return data.signature === activationSignature(data.key || "", data.store || "");
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ function httpsPost(url, body) {
80
+ return new Promise((resolve, reject) => {
81
+ const u = new URL(url);
82
+ const data = JSON.stringify(body);
83
+ const req = https.request({
84
+ hostname: u.hostname,
85
+ path: u.pathname,
86
+ method: "POST",
87
+ headers: {
88
+ "Content-Type": "application/json",
89
+ "Content-Length": Buffer.byteLength(data),
90
+ "User-Agent": "ShardStitch-Installer/1.0",
91
+ },
92
+ }, (res) => {
93
+ let body = "";
94
+ res.on("data", (c) => body += c);
95
+ res.on("end", () => {
96
+ if (res.statusCode >= 400) reject(new Error(`HTTP ${res.statusCode}: ${body}`));
97
+ else resolve(JSON.parse(body));
98
+ });
99
+ });
100
+ req.on("error", reject);
101
+ req.write(data);
102
+ req.end();
103
+ });
104
+ }
105
+
106
+ function downloadFile(url, dest) {
107
+ return new Promise((resolve, reject) => {
108
+ mkdirSync(dirname(dest), { recursive: true });
109
+ const tmp = dest + ".tmp";
110
+ const file = createWriteStream(tmp);
111
+
112
+ function follow(url) {
113
+ https.get(url, { headers: { "User-Agent": "ShardStitch-Installer/1.0" } }, (res) => {
114
+ if (res.statusCode === 301 || res.statusCode === 302) {
115
+ return follow(res.headers.location);
116
+ }
117
+ const total = parseInt(res.headers["content-length"] || "0");
118
+ let done = 0;
119
+ res.on("data", (chunk) => {
120
+ file.write(chunk);
121
+ done += chunk.length;
122
+ if (total) {
123
+ const pct = Math.floor(done * 100 / total);
124
+ process.stdout.write(`\r Downloading... ${pct}%`);
125
+ }
126
+ });
127
+ res.on("end", () => {
128
+ file.end();
129
+ process.stdout.write("\n");
130
+ renameSync(tmp, dest);
131
+ resolve();
132
+ });
133
+ res.on("error", reject);
134
+ }).on("error", reject);
135
+ }
136
+
137
+ follow(url);
138
+ });
139
+ }
140
+
141
+ async function cmdInstall(key) {
142
+ key = key.trim();
143
+ console.log();
144
+ console.log(" ShardStitch — verifying license key...");
145
+
146
+ let data;
147
+ try {
148
+ data = await httpsPost("https://api.polar.sh/v1/customer-portal/license-keys/validate", {
149
+ key,
150
+ organization_id: POLAR_ORG_ID,
151
+ });
152
+ } catch (e) {
153
+ console.log(` ✗ ${e.message.includes("404") ? "Key not found. Check for typos or email support@shardstitch.com." : "Network error: " + e.message}`);
154
+ process.exit(1);
155
+ }
156
+
157
+ if (!["granted", "active"].includes(data.status) && !data.id) {
158
+ console.log(" ✗ Invalid license key.");
159
+ process.exit(1);
160
+ }
161
+
162
+ console.log(" ✓ License valid.");
163
+ console.log();
164
+
165
+ // Find download URL from benefit grants
166
+ let downloadUrl = null;
167
+ for (const grant of data.benefit_grants || []) {
168
+ if (grant.benefit?.type === "downloadables") {
169
+ const files = grant.properties?.files || [];
170
+ for (const f of files) {
171
+ const name = (f.name || "").toLowerCase();
172
+ const url = f.url || f.download_url;
173
+ if (!url) continue;
174
+ if (PLATFORM === "windows" && name.includes(".exe")) { downloadUrl = url; break; }
175
+ if (PLATFORM === "linux" && name.includes("linux")) { downloadUrl = url; break; }
176
+ if (PLATFORM === "mac" && (name.includes("mac") || name.includes("darwin"))) { downloadUrl = url; break; }
177
+ }
178
+ if (!downloadUrl && files[0]) downloadUrl = files[0].url || files[0].download_url;
179
+ }
180
+ if (downloadUrl) break;
181
+ }
182
+
183
+ if (!downloadUrl) {
184
+ console.log(" ✗ No downloadable files found on this license.");
185
+ console.log(" Email support@shardstitch.com for help.");
186
+ process.exit(1);
187
+ }
188
+
189
+ const exePath = join(INSTALL_DIR, EXE_NAME);
190
+ console.log(` Downloading ShardStitch for ${PLATFORM}...`);
191
+
192
+ try {
193
+ await downloadFile(downloadUrl, exePath);
194
+ if (PLATFORM !== "windows") chmodSync(exePath, 0o755);
195
+ } catch (e) {
196
+ console.log(` ✗ Download failed: ${e.message}`);
197
+ process.exit(1);
198
+ }
199
+
200
+ saveActivation(key);
201
+
202
+ console.log(` ✓ Installed to ${exePath}`);
203
+ console.log();
204
+ console.log(" Run `shardstitch` to launch the dashboard.");
205
+ console.log();
206
+ }
207
+
208
+ function cmdLaunch() {
209
+ const exe = findExe();
210
+ if (exe) {
211
+ console.log(" Starting ShardStitch...");
212
+ spawn(exe, [], { detached: true, stdio: "ignore" }).unref();
213
+ console.log(" Dashboard: http://127.0.0.1:8765");
214
+ process.exit(0);
215
+ }
216
+
217
+ console.log();
218
+ console.log(" ShardStitch — move your AI coding session between tools.");
219
+ console.log();
220
+ if (isActivated()) {
221
+ console.log(" App file not found. Re-run: shardstitch install <your-key>");
222
+ } else {
223
+ console.log(" Not installed. After purchase run:");
224
+ console.log(" shardstitch install <your-license-key>");
225
+ console.log();
226
+ console.log(" Get ShardStitch at: https://shardstitch.com");
227
+ console.log(" Launch week: $19 — lifetime license, zero cloud.");
228
+ }
229
+ console.log();
230
+ process.exit(1);
231
+ }
232
+
233
+ const args = process.argv.slice(2);
234
+ if (args[0] === "install") {
235
+ if (!args[1]) {
236
+ console.log();
237
+ console.log(" Usage: shardstitch install <your-license-key>");
238
+ console.log();
239
+ console.log(" Get your key at: https://shardstitch.com");
240
+ console.log();
241
+ process.exit(1);
242
+ }
243
+ cmdInstall(args[1]).catch((e) => {
244
+ console.log(" ✗ Unexpected error:", e.message);
245
+ process.exit(1);
246
+ });
247
+ } else {
248
+ cmdLaunch();
249
+ }
package/package.json CHANGED
@@ -1,13 +1,34 @@
1
1
  {
2
2
  "name": "shardstitch",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "ShardStitch — capture your AI coding session (git diff, dependency graph, intent) and stitch it into the next tool. 22 AI tools supported. 100% local, zero telemetry. https://shardstitch.com",
5
5
  "homepage": "https://shardstitch.com",
6
- "bugs": { "url": "https://github.com/shardstitch/shardstitch/issues" },
7
- "repository": { "type": "git", "url": "git+https://github.com/shardstitch/shardstitch.git" },
8
- "keywords": ["ai", "claude", "cursor", "codex", "gemini", "context", "handoff", "rate-limit", "developer-tools"],
6
+ "bugs": {
7
+ "url": "https://github.com/shardstitch/shardstitch/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/shardstitch/shardstitch.git"
12
+ },
13
+ "keywords": [
14
+ "ai",
15
+ "claude",
16
+ "cursor",
17
+ "codex",
18
+ "gemini",
19
+ "context",
20
+ "handoff",
21
+ "rate-limit",
22
+ "developer-tools"
23
+ ],
9
24
  "author": "ShardStitch <support@shardstitch.com>",
10
25
  "license": "SEE LICENSE IN LICENSE.txt",
11
- "bin": { "shardstitch": "./cli.js" },
12
- "files": ["cli.js", "LICENSE.txt", "README.md"]
26
+ "bin": {
27
+ "shardstitch": "cli.js"
28
+ },
29
+ "files": [
30
+ "cli.js",
31
+ "LICENSE.txt",
32
+ "README.md"
33
+ ]
13
34
  }