@stithy/pod 0.1.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.
- package/LICENSE +26 -0
- package/README.md +57 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +45 -0
- package/dist/cli.js.map +1 -0
- package/dist/crypto.d.ts +15 -0
- package/dist/crypto.js +63 -0
- package/dist/crypto.js.map +1 -0
- package/dist/db.d.ts +4 -0
- package/dist/db.js +54 -0
- package/dist/db.js.map +1 -0
- package/dist/install.d.ts +2 -0
- package/dist/install.js +168 -0
- package/dist/install.js.map +1 -0
- package/dist/providers/gelato.d.ts +28 -0
- package/dist/providers/gelato.js +86 -0
- package/dist/providers/gelato.js.map +1 -0
- package/dist/providers/printful.d.ts +28 -0
- package/dist/providers/printful.js +84 -0
- package/dist/providers/printful.js.map +1 -0
- package/dist/providers/printify.d.ts +23 -0
- package/dist/providers/printify.js +100 -0
- package/dist/providers/printify.js.map +1 -0
- package/dist/providers/registry.d.ts +26 -0
- package/dist/providers/registry.js +94 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +58 -0
- package/dist/providers/types.js +21 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +353 -0
- package/dist/server.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: Stithy
|
|
6
|
+
Licensed Work: @stithy/memory
|
|
7
|
+
Copyright (c) 2026 Stithy
|
|
8
|
+
Additional Use Grant: You may use the Licensed Work for non-production
|
|
9
|
+
use, internal evaluation, and personal projects
|
|
10
|
+
free of charge. Production use by organizations
|
|
11
|
+
with annual revenue over $1M USD requires a
|
|
12
|
+
commercial license purchased through the MCP
|
|
13
|
+
Marketplace or directly from Stithy.
|
|
14
|
+
|
|
15
|
+
Change Date: 2028-04-12
|
|
16
|
+
Change License: Apache License, Version 2.0
|
|
17
|
+
|
|
18
|
+
For full BSL 1.1 terms see: https://mariadb.com/bsl11/
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
Notice
|
|
23
|
+
|
|
24
|
+
The Business Source License (this document, or the "License") is not an
|
|
25
|
+
Open Source license. However, the Licensed Work will eventually be made
|
|
26
|
+
available under an Open Source License, as stated in this License.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @stithy/pod
|
|
2
|
+
|
|
3
|
+
> Manage Printify, Printful, and Gelato from one prompt. Sync products, track orders, calculate margins — without leaving your AI client.
|
|
4
|
+
|
|
5
|
+
Stithy POD is an MCP server that wraps the official APIs of the major print-on-demand platforms behind a single conversational interface. It's not a workflow-automation tool — it's an AI-native bulk operator: "list these 50 designs across Printify and Gelato with these prices and SEO titles" in one prompt.
|
|
6
|
+
|
|
7
|
+
## Outcomes
|
|
8
|
+
|
|
9
|
+
- **One prompt to rule them all.** Sync products, push prices, pull orders across every connected platform.
|
|
10
|
+
- **See your real margins.** Cached price + cost across providers, sorted by margin %.
|
|
11
|
+
- **Stop missing low stock.** A single tool call surfaces every SKU at risk on Printify.
|
|
12
|
+
- **Credentials never leave your machine.** API keys are AES-256-GCM encrypted at rest with a per-machine master key.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npx -y @stithy/pod install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then run, in your AI client:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
configure_provider provider=printify api_key=<your key> shop_id=<your shop id>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Repeat for printful / gelato as desired.
|
|
27
|
+
|
|
28
|
+
## v0.1 Provider Coverage
|
|
29
|
+
|
|
30
|
+
| Capability | Printify | Printful | Gelato |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| List products | ✅ | ✅ | ✅ |
|
|
33
|
+
| List orders | ✅ | ✅ | ✅ |
|
|
34
|
+
| Update pricing | ✅ | — | — |
|
|
35
|
+
| Pause / unpause listing | ✅ | — | — |
|
|
36
|
+
| Stock query | ✅ | — | — |
|
|
37
|
+
|
|
38
|
+
Full Printful and Gelato write paths land in v0.2.
|
|
39
|
+
|
|
40
|
+
## Tools (12)
|
|
41
|
+
|
|
42
|
+
`configure_provider`, `list_providers`, `remove_provider`, `list_products`, `update_product_pricing`, `pause_listing`, `unpause_listing`, `list_orders`, `calculate_margins`, `low_stock_alert`, `revenue_summary`, `platform_health`.
|
|
43
|
+
|
|
44
|
+
## Security & Platform Risk
|
|
45
|
+
|
|
46
|
+
- API keys are encrypted at rest. The master key is generated per-machine and stored in `~/.stithy/.pod_master` (chmod 600). Override with `STITHY_POD_MASTER_KEY` in production.
|
|
47
|
+
- Each buyer brings their own platform API keys. Stithy POD does not syndicate API access (Printify ToS compliant — no relay of one shop's credentials to another user).
|
|
48
|
+
- We don't control Printify, Printful, or Gelato uptime, rate limits, or policy changes. The adapter pattern means a single provider going dark won't break the others.
|
|
49
|
+
- Etsy is intentionally not supported. Etsy banned new sellers from third-party POD sync in 2026; existing sellers can already use Printify/Printful/Gelato sync directly.
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
[BUSL-1.1](./LICENSE). Free for personal and commercial use under $1M annual revenue. Commercial license via mcp-marketplace.io.
|
|
54
|
+
|
|
55
|
+
## Maintained
|
|
56
|
+
|
|
57
|
+
Active maintenance, 7-day bug-fix turnaround, 30-day refund. Issues: https://github.com/resolceo-ai/stithy-mcp/issues
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const cmd = process.argv[2];
|
|
3
|
+
if (cmd === "install" || cmd === "uninstall" || cmd === "list") {
|
|
4
|
+
await import("./install.js");
|
|
5
|
+
}
|
|
6
|
+
else if (cmd === "license") {
|
|
7
|
+
const { verifyLicense } = await import("@mcp_marketplace/license");
|
|
8
|
+
const r = await verifyLicense({ slug: "stithy-pod" });
|
|
9
|
+
if (r.valid) {
|
|
10
|
+
console.log(`Licensed (server_id: ${r.server_id ?? "n/a"})`);
|
|
11
|
+
if (r.expires_at)
|
|
12
|
+
console.log(`Expires: ${r.expires_at}`);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
console.log(`Not licensed: ${r.reason ?? "unknown"}`);
|
|
16
|
+
console.log(`Set MCP_LICENSE_KEY in your environment.`);
|
|
17
|
+
console.log(`Get a key: https://mcp-marketplace.io/stithy-pod`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else if (cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
21
|
+
console.log(`stithy-pod — print-on-demand multi-platform MCP server
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
npx @stithy/pod Run the MCP server (stdio transport)
|
|
25
|
+
npx @stithy/pod install Install into all detected AI clients
|
|
26
|
+
npx @stithy/pod install --force Overwrite existing config
|
|
27
|
+
npx @stithy/pod list Show install status for each client
|
|
28
|
+
npx @stithy/pod uninstall Remove from all detected clients
|
|
29
|
+
npx @stithy/pod license Check license status
|
|
30
|
+
|
|
31
|
+
Environment:
|
|
32
|
+
MCP_LICENSE_KEY Marketplace license key (required to run server)
|
|
33
|
+
STITHY_POD_DB Path to SQLite db (default: ~/.stithy/pod.db)
|
|
34
|
+
STITHY_POD_DIR Root for db + master key (default: ~/.stithy)
|
|
35
|
+
STITHY_POD_MASTER_KEY Override master key (recommended in production)
|
|
36
|
+
STITHY_SKIP_LICENSE Set to 1 to bypass license check (dev/test only)
|
|
37
|
+
|
|
38
|
+
Docs: https://github.com/resolceo-ai/stithy-mcp
|
|
39
|
+
`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
await import("./server.js");
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;IAC/D,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;AAC/B,CAAC;KAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;IAC7B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IACnE,MAAM,CAAC,GAAG,MAAM,aAAa,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,SAAS,IAAI,KAAK,GAAG,CAAC,CAAC;QAC7D,IAAI,CAAC,CAAC,UAAU;YAAE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;KAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;CAkBb,CAAC,CAAC;AACH,CAAC;KAAM,CAAC;IACN,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the master key for credential encryption.
|
|
3
|
+
*
|
|
4
|
+
* Priority:
|
|
5
|
+
* 1. STITHY_POD_MASTER_KEY env var (recommended in production)
|
|
6
|
+
* 2. Per-machine key file at ~/.stithy/.pod_master (auto-generated, chmod 600)
|
|
7
|
+
*
|
|
8
|
+
* The file fallback keeps the server zero-config for solo users while still
|
|
9
|
+
* keeping API tokens off raw disk in clear text.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveMasterKey(keyFilePath: string): Buffer;
|
|
12
|
+
export declare function encryptString(plain: string, masterKey: Buffer): string;
|
|
13
|
+
export declare function decryptString(token: string, masterKey: Buffer): string;
|
|
14
|
+
/** Mask a credential for display in tool outputs. */
|
|
15
|
+
export declare function mask(s: string): string;
|
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
const ALGO = "aes-256-gcm";
|
|
5
|
+
const KEY_LEN = 32;
|
|
6
|
+
const IV_LEN = 12;
|
|
7
|
+
const SALT_LEN = 16;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the master key for credential encryption.
|
|
10
|
+
*
|
|
11
|
+
* Priority:
|
|
12
|
+
* 1. STITHY_POD_MASTER_KEY env var (recommended in production)
|
|
13
|
+
* 2. Per-machine key file at ~/.stithy/.pod_master (auto-generated, chmod 600)
|
|
14
|
+
*
|
|
15
|
+
* The file fallback keeps the server zero-config for solo users while still
|
|
16
|
+
* keeping API tokens off raw disk in clear text.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveMasterKey(keyFilePath) {
|
|
19
|
+
const env = process.env.STITHY_POD_MASTER_KEY;
|
|
20
|
+
if (env && env.length >= 32) {
|
|
21
|
+
return scryptSync(env, "stithy-pod", KEY_LEN);
|
|
22
|
+
}
|
|
23
|
+
if (!existsSync(keyFilePath)) {
|
|
24
|
+
mkdirSync(dirname(keyFilePath), { recursive: true });
|
|
25
|
+
const seed = randomBytes(48).toString("hex");
|
|
26
|
+
writeFileSync(keyFilePath, seed);
|
|
27
|
+
try {
|
|
28
|
+
chmodSync(keyFilePath, 0o600);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Windows: chmod is a no-op
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const seed = readFileSync(keyFilePath, "utf8").trim();
|
|
35
|
+
return scryptSync(seed, "stithy-pod", KEY_LEN);
|
|
36
|
+
}
|
|
37
|
+
export function encryptString(plain, masterKey) {
|
|
38
|
+
const salt = randomBytes(SALT_LEN);
|
|
39
|
+
const iv = randomBytes(IV_LEN);
|
|
40
|
+
const key = scryptSync(masterKey, salt, KEY_LEN);
|
|
41
|
+
const cipher = createCipheriv(ALGO, key, iv);
|
|
42
|
+
const enc = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
|
|
43
|
+
const tag = cipher.getAuthTag();
|
|
44
|
+
return Buffer.concat([salt, iv, tag, enc]).toString("base64");
|
|
45
|
+
}
|
|
46
|
+
export function decryptString(token, masterKey) {
|
|
47
|
+
const buf = Buffer.from(token, "base64");
|
|
48
|
+
const salt = buf.subarray(0, SALT_LEN);
|
|
49
|
+
const iv = buf.subarray(SALT_LEN, SALT_LEN + IV_LEN);
|
|
50
|
+
const tag = buf.subarray(SALT_LEN + IV_LEN, SALT_LEN + IV_LEN + 16);
|
|
51
|
+
const enc = buf.subarray(SALT_LEN + IV_LEN + 16);
|
|
52
|
+
const key = scryptSync(masterKey, salt, KEY_LEN);
|
|
53
|
+
const decipher = createDecipheriv(ALGO, key, iv);
|
|
54
|
+
decipher.setAuthTag(tag);
|
|
55
|
+
return Buffer.concat([decipher.update(enc), decipher.final()]).toString("utf8");
|
|
56
|
+
}
|
|
57
|
+
/** Mask a credential for display in tool outputs. */
|
|
58
|
+
export function mask(s) {
|
|
59
|
+
if (s.length <= 8)
|
|
60
|
+
return "***";
|
|
61
|
+
return `${s.slice(0, 4)}…${s.slice(-4)}`;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,IAAI,GAAG,aAAa,CAAC;AAC3B,MAAM,OAAO,GAAG,EAAE,CAAC;AACnB,MAAM,MAAM,GAAG,EAAE,CAAC;AAClB,MAAM,QAAQ,GAAG,EAAE,CAAC;AAEpB;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAC9C,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC5B,OAAO,UAAU,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC7C,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC;YACH,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,OAAO,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,SAAiB;IAC5D,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,SAAiB;IAC5D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,EAAE,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,GAAG,EAAE,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACjD,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAClF,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,IAAI,CAAC,CAAS;IAC5B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3C,CAAC"}
|
package/dist/db.d.ts
ADDED
package/dist/db.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
export function openDb(path) {
|
|
5
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
6
|
+
const db = new Database(path);
|
|
7
|
+
db.pragma("journal_mode = WAL");
|
|
8
|
+
db.pragma("synchronous = NORMAL");
|
|
9
|
+
db.pragma("foreign_keys = ON");
|
|
10
|
+
migrate(db);
|
|
11
|
+
return db;
|
|
12
|
+
}
|
|
13
|
+
function migrate(db) {
|
|
14
|
+
db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS providers (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
18
|
+
api_key_enc TEXT NOT NULL,
|
|
19
|
+
shop_id TEXT,
|
|
20
|
+
meta TEXT NOT NULL DEFAULT '{}',
|
|
21
|
+
created_at INTEGER NOT NULL,
|
|
22
|
+
updated_at INTEGER NOT NULL
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
CREATE TABLE IF NOT EXISTS product_cache (
|
|
26
|
+
provider TEXT NOT NULL,
|
|
27
|
+
external_id TEXT NOT NULL,
|
|
28
|
+
title TEXT NOT NULL,
|
|
29
|
+
price_cents INTEGER,
|
|
30
|
+
cost_cents INTEGER,
|
|
31
|
+
stock INTEGER,
|
|
32
|
+
status TEXT,
|
|
33
|
+
raw TEXT NOT NULL,
|
|
34
|
+
synced_at INTEGER NOT NULL,
|
|
35
|
+
PRIMARY KEY (provider, external_id)
|
|
36
|
+
);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_product_synced ON product_cache(synced_at DESC);
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS order_cache (
|
|
40
|
+
provider TEXT NOT NULL,
|
|
41
|
+
external_id TEXT NOT NULL,
|
|
42
|
+
status TEXT,
|
|
43
|
+
total_cents INTEGER,
|
|
44
|
+
currency TEXT,
|
|
45
|
+
placed_at INTEGER,
|
|
46
|
+
raw TEXT NOT NULL,
|
|
47
|
+
synced_at INTEGER NOT NULL,
|
|
48
|
+
PRIMARY KEY (provider, external_id)
|
|
49
|
+
);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_order_placed ON order_cache(placed_at DESC);
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
export const PROVIDER_IDS = ["printify", "printful", "gelato"];
|
|
54
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,OAAO,CAAC,EAAqB;IACpC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCP,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAU,CAAC"}
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, renameSync } from "node:fs";
|
|
3
|
+
import { homedir, platform } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
function targets() {
|
|
6
|
+
const home = homedir();
|
|
7
|
+
const p = platform();
|
|
8
|
+
const list = [];
|
|
9
|
+
if (p === "darwin") {
|
|
10
|
+
list.push({
|
|
11
|
+
name: "Claude Desktop",
|
|
12
|
+
configPath: join(home, "Library/Application Support/Claude/claude_desktop_config.json"),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
else if (p === "win32") {
|
|
16
|
+
const appdata = process.env.APPDATA ?? join(home, "AppData/Roaming");
|
|
17
|
+
list.push({
|
|
18
|
+
name: "Claude Desktop",
|
|
19
|
+
configPath: join(appdata, "Claude/claude_desktop_config.json"),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
list.push({
|
|
24
|
+
name: "Claude Desktop",
|
|
25
|
+
configPath: join(home, ".config/Claude/claude_desktop_config.json"),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
list.push({ name: "Cursor", configPath: join(home, ".cursor/mcp.json") });
|
|
29
|
+
list.push({
|
|
30
|
+
name: "Windsurf",
|
|
31
|
+
configPath: join(home, ".codeium/windsurf/mcp_config.json"),
|
|
32
|
+
});
|
|
33
|
+
list.push({ name: "Claude Code", configPath: join(home, ".claude.json") });
|
|
34
|
+
return list;
|
|
35
|
+
}
|
|
36
|
+
function serverEntry() {
|
|
37
|
+
return { command: "npx", args: ["-y", "@stithy/pod"] };
|
|
38
|
+
}
|
|
39
|
+
function readJson(path) {
|
|
40
|
+
if (!existsSync(path))
|
|
41
|
+
return { ok: true, data: {} };
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync(path, "utf8");
|
|
44
|
+
if (raw.trim().length === 0)
|
|
45
|
+
return { ok: true, data: {} };
|
|
46
|
+
const parsed = JSON.parse(raw);
|
|
47
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
48
|
+
return { ok: false, reason: "Existing config is not a JSON object — refusing to overwrite" };
|
|
49
|
+
}
|
|
50
|
+
return { ok: true, data: parsed };
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
reason: `Existing config is not valid JSON (${e instanceof Error ? e.message : String(e)}) — refusing to overwrite`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function writeJsonAtomic(path, data) {
|
|
60
|
+
const tmp = `${path}.stithy.tmp`;
|
|
61
|
+
const json = JSON.stringify(data, null, 2);
|
|
62
|
+
writeFileSync(tmp, json);
|
|
63
|
+
JSON.parse(readFileSync(tmp, "utf8"));
|
|
64
|
+
renameSync(tmp, path);
|
|
65
|
+
}
|
|
66
|
+
function backup(path) {
|
|
67
|
+
if (!existsSync(path))
|
|
68
|
+
return;
|
|
69
|
+
const bak = `${path}.stithy.bak.${Date.now()}`;
|
|
70
|
+
copyFileSync(path, bak);
|
|
71
|
+
console.log(` backup: ${bak}`);
|
|
72
|
+
}
|
|
73
|
+
function installFor(t, name, force) {
|
|
74
|
+
try {
|
|
75
|
+
const result = readJson(t.configPath);
|
|
76
|
+
if (!result.ok) {
|
|
77
|
+
console.error(`✗ ${t.name}: ${result.reason}`);
|
|
78
|
+
return "error";
|
|
79
|
+
}
|
|
80
|
+
const cfg = result.data;
|
|
81
|
+
const servers = cfg["mcpServers"] ?? {};
|
|
82
|
+
if (servers[name] && !force) {
|
|
83
|
+
console.log(`✓ ${t.name}: already configured (use --force to overwrite)`);
|
|
84
|
+
return "skipped";
|
|
85
|
+
}
|
|
86
|
+
servers[name] = serverEntry();
|
|
87
|
+
cfg["mcpServers"] = servers;
|
|
88
|
+
if (existsSync(t.configPath))
|
|
89
|
+
backup(t.configPath);
|
|
90
|
+
mkdirSync(dirname(t.configPath), { recursive: true });
|
|
91
|
+
writeJsonAtomic(t.configPath, cfg);
|
|
92
|
+
console.log(`✓ ${t.name}: installed at ${t.configPath}`);
|
|
93
|
+
return "installed";
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.error(`✗ ${t.name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
97
|
+
return "error";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function uninstallFor(t, name) {
|
|
101
|
+
try {
|
|
102
|
+
if (!existsSync(t.configPath))
|
|
103
|
+
return false;
|
|
104
|
+
const result = readJson(t.configPath);
|
|
105
|
+
if (!result.ok) {
|
|
106
|
+
console.error(`✗ ${t.name}: ${result.reason}`);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const cfg = result.data;
|
|
110
|
+
const servers = cfg["mcpServers"] ?? {};
|
|
111
|
+
if (!servers[name])
|
|
112
|
+
return false;
|
|
113
|
+
delete servers[name];
|
|
114
|
+
cfg["mcpServers"] = servers;
|
|
115
|
+
backup(t.configPath);
|
|
116
|
+
writeJsonAtomic(t.configPath, cfg);
|
|
117
|
+
console.log(`✓ ${t.name}: removed`);
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
console.error(`✗ ${t.name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function main() {
|
|
126
|
+
const args = process.argv.slice(2);
|
|
127
|
+
const cmd = args[0] ?? "install";
|
|
128
|
+
const force = args.includes("--force");
|
|
129
|
+
const name = "stithy-pod";
|
|
130
|
+
if (cmd === "uninstall") {
|
|
131
|
+
console.log("Removing stithy-pod from detected clients...");
|
|
132
|
+
for (const t of targets())
|
|
133
|
+
uninstallFor(t, name);
|
|
134
|
+
console.log("\nDone. Restart your AI client to pick up the change.");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (cmd === "list") {
|
|
138
|
+
for (const t of targets()) {
|
|
139
|
+
const present = existsSync(t.configPath);
|
|
140
|
+
let has = false;
|
|
141
|
+
let bad = false;
|
|
142
|
+
if (present) {
|
|
143
|
+
const r = readJson(t.configPath);
|
|
144
|
+
if (!r.ok) {
|
|
145
|
+
bad = true;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const servers = r.data["mcpServers"] ?? {};
|
|
149
|
+
has = Boolean(servers[name]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const marker = bad ? "?" : has ? "✓" : present ? "·" : "—";
|
|
153
|
+
console.log(`${marker} ${t.name.padEnd(16)} ${t.configPath}${bad ? " (config unparseable)" : ""}`);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log("Installing stithy-pod across detected AI clients...\n");
|
|
158
|
+
let installed = 0;
|
|
159
|
+
for (const t of targets()) {
|
|
160
|
+
const result = installFor(t, name, force);
|
|
161
|
+
if (result === "installed")
|
|
162
|
+
installed++;
|
|
163
|
+
}
|
|
164
|
+
console.log(`\nInstalled to ${installed} client${installed === 1 ? "" : "s"}.`);
|
|
165
|
+
console.log("Restart your AI client(s) to load stithy-pod.");
|
|
166
|
+
}
|
|
167
|
+
main();
|
|
168
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Provider, ProviderCapabilities, ProductSummary, OrderSummary } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Gelato adapter — minimal v0.1 surface. Gelato's API is split across multiple
|
|
4
|
+
* subdomains (product, order, ecommerce) and uses store-id paths. We expose
|
|
5
|
+
* read-only product/order listing + ping. Mutations are unsupported in v0.1.
|
|
6
|
+
*/
|
|
7
|
+
export declare class GelatoProvider implements Provider {
|
|
8
|
+
private readonly apiKey;
|
|
9
|
+
private readonly storeId;
|
|
10
|
+
readonly id = "gelato";
|
|
11
|
+
readonly displayName = "Gelato";
|
|
12
|
+
readonly capabilities: ProviderCapabilities;
|
|
13
|
+
constructor(apiKey: string, storeId: string);
|
|
14
|
+
private req;
|
|
15
|
+
ping(): Promise<{
|
|
16
|
+
ok: boolean;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}>;
|
|
19
|
+
listProducts(opts?: {
|
|
20
|
+
limit?: number;
|
|
21
|
+
}): Promise<ProductSummary[]>;
|
|
22
|
+
updatePricing(): Promise<void>;
|
|
23
|
+
pauseListing(): Promise<void>;
|
|
24
|
+
unpauseListing(): Promise<void>;
|
|
25
|
+
listOrders(opts?: {
|
|
26
|
+
limit?: number;
|
|
27
|
+
}): Promise<OrderSummary[]>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ProviderUnsupportedError } from "./types.js";
|
|
2
|
+
const GELATO_PRODUCT_BASE = "https://product.gelatoapis.com/v3";
|
|
3
|
+
const GELATO_ORDER_BASE = "https://order.gelatoapis.com/v4";
|
|
4
|
+
/**
|
|
5
|
+
* Gelato adapter — minimal v0.1 surface. Gelato's API is split across multiple
|
|
6
|
+
* subdomains (product, order, ecommerce) and uses store-id paths. We expose
|
|
7
|
+
* read-only product/order listing + ping. Mutations are unsupported in v0.1.
|
|
8
|
+
*/
|
|
9
|
+
export class GelatoProvider {
|
|
10
|
+
apiKey;
|
|
11
|
+
storeId;
|
|
12
|
+
id = "gelato";
|
|
13
|
+
displayName = "Gelato";
|
|
14
|
+
capabilities = {
|
|
15
|
+
list_products: true,
|
|
16
|
+
update_pricing: false,
|
|
17
|
+
list_orders: true,
|
|
18
|
+
pause_listing: false,
|
|
19
|
+
stock_query: false,
|
|
20
|
+
create_product: false,
|
|
21
|
+
};
|
|
22
|
+
constructor(apiKey, storeId) {
|
|
23
|
+
this.apiKey = apiKey;
|
|
24
|
+
this.storeId = storeId;
|
|
25
|
+
}
|
|
26
|
+
async req(url, init = {}) {
|
|
27
|
+
const res = await fetch(url, {
|
|
28
|
+
...init,
|
|
29
|
+
headers: {
|
|
30
|
+
"X-API-KEY": this.apiKey,
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
...(init.headers ?? {}),
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const body = await res.text().catch(() => "");
|
|
37
|
+
throw new Error(`Gelato ${res.status} on ${url}: ${body.slice(0, 300)}`);
|
|
38
|
+
}
|
|
39
|
+
return (await res.json());
|
|
40
|
+
}
|
|
41
|
+
async ping() {
|
|
42
|
+
try {
|
|
43
|
+
await this.req(`${GELATO_PRODUCT_BASE}/stores/${this.storeId}/products?limit=1`);
|
|
44
|
+
return { ok: true };
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
return { ok: false, reason: e instanceof Error ? e.message : String(e) };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async listProducts(opts) {
|
|
51
|
+
const limit = Math.min(opts?.limit ?? 50, 100);
|
|
52
|
+
const data = await this.req(`${GELATO_PRODUCT_BASE}/stores/${this.storeId}/products?limit=${limit}`);
|
|
53
|
+
return (data.products ?? []).map((p) => ({
|
|
54
|
+
external_id: p.id,
|
|
55
|
+
title: p.title ?? "(untitled)",
|
|
56
|
+
price_cents: null,
|
|
57
|
+
cost_cents: null,
|
|
58
|
+
stock: null,
|
|
59
|
+
status: p.status ?? "unknown",
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
updatePricing() {
|
|
63
|
+
throw new ProviderUnsupportedError(this.id, "update_pricing");
|
|
64
|
+
}
|
|
65
|
+
pauseListing() {
|
|
66
|
+
throw new ProviderUnsupportedError(this.id, "pause_listing");
|
|
67
|
+
}
|
|
68
|
+
unpauseListing() {
|
|
69
|
+
throw new ProviderUnsupportedError(this.id, "pause_listing");
|
|
70
|
+
}
|
|
71
|
+
async listOrders(opts) {
|
|
72
|
+
const limit = Math.min(opts?.limit ?? 50, 100);
|
|
73
|
+
const data = await this.req(`${GELATO_ORDER_BASE}/orders:search`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
body: JSON.stringify({ storeIds: [this.storeId], limit }),
|
|
76
|
+
});
|
|
77
|
+
return (data.orders ?? []).map((o) => ({
|
|
78
|
+
external_id: o.id,
|
|
79
|
+
status: o.fulfillmentStatus ?? null,
|
|
80
|
+
total_cents: o.itemsRetailPriceSum !== undefined ? Math.round(o.itemsRetailPriceSum * 100) : null,
|
|
81
|
+
currency: o.currency ?? "USD",
|
|
82
|
+
placed_at: o.createdAt ? Date.parse(o.createdAt) || null : null,
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=gelato.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gelato.js","sourceRoot":"","sources":["../../src/providers/gelato.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Provider, ProviderCapabilities, ProductSummary, OrderSummary } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Printful adapter — read paths only in v0.1 (list_products, list_orders, ping).
|
|
4
|
+
* Pricing updates and pause/unpause require Printful's per-variant sync flow that
|
|
5
|
+
* needs more careful handling. Returns ProviderUnsupportedError for those ops so
|
|
6
|
+
* the caller can fall back gracefully.
|
|
7
|
+
*/
|
|
8
|
+
export declare class PrintfulProvider implements Provider {
|
|
9
|
+
private readonly apiKey;
|
|
10
|
+
readonly id = "printful";
|
|
11
|
+
readonly displayName = "Printful";
|
|
12
|
+
readonly capabilities: ProviderCapabilities;
|
|
13
|
+
constructor(apiKey: string);
|
|
14
|
+
private req;
|
|
15
|
+
ping(): Promise<{
|
|
16
|
+
ok: boolean;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}>;
|
|
19
|
+
listProducts(opts?: {
|
|
20
|
+
limit?: number;
|
|
21
|
+
}): Promise<ProductSummary[]>;
|
|
22
|
+
updatePricing(): Promise<void>;
|
|
23
|
+
pauseListing(): Promise<void>;
|
|
24
|
+
unpauseListing(): Promise<void>;
|
|
25
|
+
listOrders(opts?: {
|
|
26
|
+
limit?: number;
|
|
27
|
+
}): Promise<OrderSummary[]>;
|
|
28
|
+
}
|