create-snipara 1.3.21 → 1.3.23

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 +13 -0
  2. package/dist/index.js +334 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -197,6 +197,19 @@ Writes `.snipara/companion/doctor.json` and validates:
197
197
  - Snipara Sandbox CLI presence
198
198
  - environment files
199
199
  - hosted MCP reachability when a live API key is available
200
+ - exposed Hosted MCP tool aliases (`snipara_*`, with `rlm_*` reported as legacy)
201
+ - client-specific loading gaps that the terminal cannot prove
202
+
203
+ For Claude-specific support, run:
204
+
205
+ ```bash
206
+ npx create-snipara doctor --client claude
207
+ ```
208
+
209
+ This separates "Snipara config was written" from "Claude actually loaded the MCP
210
+ server." If Claude uses its cloud/UI MCP configuration instead of local files,
211
+ the doctor output points the user to Claude **Settings -> MCP** instead of
212
+ repeating local file edits.
200
213
 
201
214
  ### Repair local setup
202
215
 
package/dist/index.js CHANGED
@@ -9,7 +9,81 @@ import fs from "fs";
9
9
  import path from "path";
10
10
  import { execSync, spawn } from "child_process";
11
11
  import { homedir } from "os";
12
- var VERSION = "1.3.20";
12
+
13
+ // package.json
14
+ var package_default = {
15
+ name: "create-snipara",
16
+ version: "1.3.23",
17
+ description: "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
18
+ main: "dist/index.js",
19
+ type: "module",
20
+ bin: {
21
+ "create-snipara": "dist/index.js"
22
+ },
23
+ scripts: {
24
+ build: "tsup src/index.ts --format esm --dts --clean",
25
+ dev: "tsup src/index.ts --format esm --watch",
26
+ prepublishOnly: "npm run build",
27
+ pretest: "pnpm build",
28
+ test: "node --test test/*.test.cjs",
29
+ "pack:smoke": "bash ../../scripts/smoke-node-package.sh packages/create-snipara create-snipara create-snipara --help",
30
+ "type-check": "tsc --noEmit",
31
+ lint: "eslint src --ext .ts"
32
+ },
33
+ keywords: [
34
+ "snipara",
35
+ "create-snipara",
36
+ "mcp",
37
+ "snipara-sandbox",
38
+ "context-optimization",
39
+ "llm",
40
+ "ai-agents",
41
+ "multi-agent",
42
+ "memory",
43
+ "claude-code",
44
+ "codex",
45
+ "chatgpt",
46
+ "openai",
47
+ "cursor",
48
+ "windsurf",
49
+ "gemini",
50
+ "vscode",
51
+ "continue",
52
+ "claude-desktop"
53
+ ],
54
+ author: "Snipara",
55
+ license: "MIT",
56
+ repository: {
57
+ type: "git",
58
+ url: "git+https://github.com/Snipara/create-snipara.git"
59
+ },
60
+ homepage: "https://snipara.com/docs/integration/create-snipara",
61
+ dependencies: {
62
+ chalk: "^5.3.0",
63
+ commander: "^12.1.0",
64
+ ora: "^8.0.1",
65
+ prompts: "^2.4.2"
66
+ },
67
+ devDependencies: {
68
+ "@eslint/js": "^9.39.3",
69
+ "@types/node": "^20.17.12",
70
+ "@types/prompts": "^2.4.9",
71
+ "@typescript-eslint/eslint-plugin": "^8.56.0",
72
+ "@typescript-eslint/parser": "^8.56.0",
73
+ eslint: "^9.39.3",
74
+ tsup: "^8.3.5",
75
+ typescript: "^5.7.2"
76
+ },
77
+ engines: {
78
+ node: ">=18"
79
+ },
80
+ files: [
81
+ "dist"
82
+ ]
83
+ };
84
+
85
+ // src/index.ts
86
+ var VERSION = package_default.version;
13
87
  var BASE_URL = "https://www.snipara.com";
14
88
  var API_URL = "https://api.snipara.com";
15
89
  var COMPANION_PACKAGE_NAME = "snipara-companion";
@@ -100,7 +174,10 @@ function buildProgram() {
100
174
  printBanner();
101
175
  await runInit(options);
102
176
  });
103
- program.command("doctor").description("Inspect the current project and write a validation report").option("--json", "Print the report as JSON").action(async (options) => {
177
+ program.command("doctor").description("Inspect the current project and write a validation report").option(
178
+ "--client <client>",
179
+ "Run client-specific diagnostics: claude, claude-code, claude-desktop, cursor, codex, etc."
180
+ ).option("--skip-npm-check", "Skip npm latest-version freshness check").option("--json", "Print the report as JSON").action(async (options) => {
104
181
  printBanner();
105
182
  await runDoctor(options);
106
183
  });
@@ -217,8 +294,16 @@ async function runInit(options) {
217
294
  }
218
295
  }
219
296
  async function runDoctor(options) {
220
- const answers = loadExistingAnswers(options);
221
- const report = await runValidation(answers, true);
297
+ const requestedClient = normalizeClientOption(options.client);
298
+ const existingAnswers = loadExistingAnswers(options);
299
+ const answers = {
300
+ ...existingAnswers,
301
+ aiClient: requestedClient || existingAnswers.aiClient
302
+ };
303
+ const report = await runValidation(answers, true, {
304
+ client: requestedClient || answers.aiClient,
305
+ includeNpmFreshness: !options.skipNpmCheck
306
+ });
222
307
  writeDoctorReport(report);
223
308
  if (options.json) {
224
309
  console.log(JSON.stringify({ detection: detectEnvironment(), validation: report }, null, 2));
@@ -557,6 +642,34 @@ function resolveRequestedProfile(options) {
557
642
  function isInstallProfile(value) {
558
643
  return ["hosted-only", "hosted-companion", "full-stack", "runtime-only"].includes(value);
559
644
  }
645
+ function normalizeClientOption(value) {
646
+ if (!value) {
647
+ return null;
648
+ }
649
+ const normalized = value.trim().toLowerCase();
650
+ const aliases = {
651
+ claude: "claude-code",
652
+ "claude-code": "claude-code",
653
+ claude_code: "claude-code",
654
+ claudecli: "claude-code",
655
+ "claude-cli": "claude-code",
656
+ "claude-desktop": "claude-desktop",
657
+ claude_desktop: "claude-desktop",
658
+ cursor: "cursor",
659
+ windsurf: "windsurf",
660
+ codex: "codex",
661
+ gemini: "gemini",
662
+ chatgpt: "chatgpt",
663
+ vscode: "vscode",
664
+ continue: "continue",
665
+ other: "other"
666
+ };
667
+ const client = aliases[normalized];
668
+ if (!client) {
669
+ throw new Error(`Unknown client '${value}'. Use claude, claude-code, cursor, codex, or other.`);
670
+ }
671
+ return client;
672
+ }
560
673
  function installProfileChoices() {
561
674
  return [
562
675
  {
@@ -1970,15 +2083,143 @@ function buildGitHubAutomationUrl(answers) {
1970
2083
  }
1971
2084
  return url.toString();
1972
2085
  }
1973
- async function runValidation(answers, includeHostedCheck) {
2086
+ function getClaudeClientChecks(answers, detection) {
2087
+ const checks = [];
2088
+ const claudePaths = getClaudeConfigPaths();
2089
+ const projectConfig = readJsonFile(path.join(process.cwd(), ".mcp.json"));
2090
+ const codeSettings = readJsonFile(claudePaths.codeSettingsPath);
2091
+ const desktopConfig = readJsonFile(claudePaths.desktopConfigPath);
2092
+ const projectHasSnipara = containsSniparaMcpReference(projectConfig);
2093
+ const codeSettingsHasSnipara = containsSniparaMcpReference(codeSettings);
2094
+ const desktopHasSnipara = containsSniparaMcpReference(desktopConfig);
2095
+ checks.push({
2096
+ name: "claude_project_config",
2097
+ status: projectHasSnipara ? "pass" : "warn",
2098
+ detail: projectHasSnipara ? "Project .mcp.json contains a Snipara Hosted MCP server" : "Project .mcp.json does not contain a Snipara Hosted MCP server"
2099
+ });
2100
+ if (answers.aiClient === "claude-code") {
2101
+ checks.push({
2102
+ name: "claude_code_settings",
2103
+ status: detection.files.claudeSettings ? codeSettingsHasSnipara ? "pass" : "warn" : "warn",
2104
+ detail: detection.files.claudeSettings ? codeSettingsHasSnipara ? "~/.claude/settings.json references Snipara" : "~/.claude/settings.json exists but no Snipara MCP server was found" : "~/.claude/settings.json not found; Claude may require Settings -> MCP UI setup"
2105
+ });
2106
+ }
2107
+ if (answers.aiClient === "claude-desktop") {
2108
+ checks.push({
2109
+ name: "claude_desktop_config",
2110
+ status: detection.files.claudeDesktopConfig ? desktopHasSnipara ? "pass" : "warn" : "warn",
2111
+ detail: detection.files.claudeDesktopConfig ? desktopHasSnipara ? "Claude Desktop config references Snipara" : "Claude Desktop config exists but no Snipara MCP server was found" : "Claude Desktop config not found"
2112
+ });
2113
+ }
2114
+ checks.push({
2115
+ name: "claude_tools_visible",
2116
+ status: "warn",
2117
+ detail: "This terminal cannot prove Claude loaded the MCP server; confirm Snipara tools in Claude Settings -> MCP or ToolSearch after restart"
2118
+ });
2119
+ return checks;
2120
+ }
2121
+ function readJsonFile(filePath) {
2122
+ if (!fs.existsSync(filePath)) {
2123
+ return null;
2124
+ }
2125
+ try {
2126
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
2127
+ } catch {
2128
+ return null;
2129
+ }
2130
+ }
2131
+ function containsSniparaMcpReference(value) {
2132
+ if (value === null || value === void 0) {
2133
+ return false;
2134
+ }
2135
+ if (typeof value === "string") {
2136
+ return value.includes("api.snipara.com/mcp") || value.includes("snipara-mcp");
2137
+ }
2138
+ if (Array.isArray(value)) {
2139
+ return value.some((item) => containsSniparaMcpReference(item));
2140
+ }
2141
+ if (typeof value === "object") {
2142
+ const record = value;
2143
+ const mcpServers = record.mcpServers;
2144
+ if (mcpServers && typeof mcpServers === "object" && "snipara" in mcpServers) {
2145
+ return true;
2146
+ }
2147
+ return Object.values(record).some((nested) => containsSniparaMcpReference(nested));
2148
+ }
2149
+ return false;
2150
+ }
2151
+ function getCreateSniparaPackageFreshnessCheck() {
2152
+ if (!commandExists("npm")) {
2153
+ return {
2154
+ name: "create_snipara_npm_version",
2155
+ status: "warn",
2156
+ detail: "npm not found; could not compare local create-snipara with npm latest"
2157
+ };
2158
+ }
2159
+ try {
2160
+ const raw = execSync("npm view create-snipara version --json", {
2161
+ encoding: "utf-8",
2162
+ stdio: ["ignore", "pipe", "ignore"],
2163
+ shell: "/bin/sh",
2164
+ timeout: 4e3
2165
+ }).trim();
2166
+ const latest = raw.replace(/^"|"$/g, "");
2167
+ const comparison = compareSemver(VERSION, latest);
2168
+ if (comparison === 0) {
2169
+ return {
2170
+ name: "create_snipara_npm_version",
2171
+ status: "pass",
2172
+ detail: `Local package version ${VERSION} matches npm latest`
2173
+ };
2174
+ }
2175
+ if (comparison > 0) {
2176
+ return {
2177
+ name: "create_snipara_npm_version",
2178
+ status: "warn",
2179
+ detail: `Local package version ${VERSION} is newer than npm latest ${latest}; publish may be pending`
2180
+ };
2181
+ }
2182
+ return {
2183
+ name: "create_snipara_npm_version",
2184
+ status: "warn",
2185
+ detail: `npm latest is ${latest}; rerun with npx create-snipara@latest if this local copy is older`
2186
+ };
2187
+ } catch {
2188
+ return {
2189
+ name: "create_snipara_npm_version",
2190
+ status: "warn",
2191
+ detail: "Could not reach npm to compare create-snipara latest version"
2192
+ };
2193
+ }
2194
+ }
2195
+ function compareSemver(left, right) {
2196
+ const leftParts = left.split(".").map((part) => Number.parseInt(part, 10) || 0);
2197
+ const rightParts = right.split(".").map((part) => Number.parseInt(part, 10) || 0);
2198
+ const length = Math.max(leftParts.length, rightParts.length);
2199
+ for (let index = 0; index < length; index += 1) {
2200
+ const diff = (leftParts[index] || 0) - (rightParts[index] || 0);
2201
+ if (diff !== 0) {
2202
+ return diff > 0 ? 1 : -1;
2203
+ }
2204
+ }
2205
+ return 0;
2206
+ }
2207
+ async function runValidation(answers, includeHostedCheck, options = {}) {
1974
2208
  console.log(chalk.bold("\n\u{1FA7A} Running Validation\n"));
1975
2209
  const checks = [];
1976
2210
  const detection = detectEnvironment();
2211
+ const targetClient = options.client || answers.aiClient;
2212
+ const effectiveAnswers = { ...answers, aiClient: targetClient };
1977
2213
  checks.push({
1978
2214
  name: "project_slug",
1979
2215
  status: answers.projectSlug ? "pass" : "fail",
1980
2216
  detail: answers.projectSlug || "Project slug missing"
1981
2217
  });
2218
+ checks.push({
2219
+ name: "target_client",
2220
+ status: "pass",
2221
+ detail: getClientDisplayName(targetClient)
2222
+ });
1982
2223
  checks.push({
1983
2224
  name: "mcp_config",
1984
2225
  status: detection.files.mcpConfig ? "pass" : "warn",
@@ -2016,9 +2257,15 @@ async function runValidation(answers, includeHostedCheck) {
2016
2257
  });
2017
2258
  checks.push({
2018
2259
  name: "hooks",
2019
- status: answers.installHooks && answers.aiClient === "claude-code" ? detection.files.claudeDir ? "pass" : "warn" : "pass",
2020
- detail: answers.installHooks && answers.aiClient === "claude-code" ? detection.files.claudeDir ? "~/.claude detected" : "~/.claude missing" : "Hooks not requested"
2260
+ status: answers.installHooks && targetClient === "claude-code" ? detection.files.claudeDir ? "pass" : "warn" : "pass",
2261
+ detail: answers.installHooks && targetClient === "claude-code" ? detection.files.claudeDir ? "~/.claude detected" : "~/.claude missing" : "Hooks not requested"
2021
2262
  });
2263
+ if (targetClient === "claude-code" || targetClient === "claude-desktop") {
2264
+ checks.push(...getClaudeClientChecks(effectiveAnswers, detection));
2265
+ }
2266
+ if (options.includeNpmFreshness) {
2267
+ checks.push(getCreateSniparaPackageFreshnessCheck());
2268
+ }
2022
2269
  const githubRepo = answers.githubRepo || detectGitRepository();
2023
2270
  checks.push({
2024
2271
  name: "github_answer_packs",
@@ -2051,11 +2298,12 @@ function writeDoctorReport(report) {
2051
2298
  console.log(chalk.green(" \u2713 Wrote .snipara/companion/doctor.json"));
2052
2299
  }
2053
2300
  function printDoctorReport(detection, report) {
2301
+ const targetClient = report.checks.find((check) => check.name === "target_client")?.detail || detection.aiClient;
2054
2302
  console.log(chalk.bold("\n\u{1FA7A} Snipara Doctor\n"));
2055
2303
  console.log(
2056
2304
  ` Project slug: ${detection.existingConfig.slug || detection.projectSlug || "unknown"}`
2057
2305
  );
2058
- console.log(` AI client: ${detection.aiClient}`);
2306
+ console.log(` AI client: ${targetClient}`);
2059
2307
  console.log(` In git repo: ${detection.inGitRepo ? "yes" : "no"}`);
2060
2308
  console.log(` Profile: ${report.profile}`);
2061
2309
  console.log("");
@@ -2090,22 +2338,31 @@ async function testHostedConnection(answers) {
2090
2338
  ];
2091
2339
  }
2092
2340
  spinner.succeed("Connected to Snipara Hosted MCP.");
2341
+ const toolNames = extractMcpToolNames(toolsResponse.data);
2342
+ const hasSniparaTools = toolNames.some((name) => name.startsWith("snipara_"));
2343
+ const hasLegacyTools = toolNames.some((name) => name.startsWith("rlm_"));
2093
2344
  const checks = [
2094
2345
  {
2095
2346
  name: "hosted_connection",
2096
2347
  status: "pass",
2097
2348
  detail: "tools/list succeeded"
2349
+ },
2350
+ {
2351
+ name: "hosted_tool_aliases",
2352
+ status: hasSniparaTools ? "pass" : hasLegacyTools ? "warn" : "warn",
2353
+ detail: hasSniparaTools ? "snipara_* tools are exposed by Hosted MCP" : hasLegacyTools ? "Only legacy rlm_* tool aliases were detected; Hosted MCP is usable but naming is stale" : "tools/list succeeded, but expected snipara_* tools were not detected"
2098
2354
  }
2099
2355
  ];
2356
+ const settingsTool = hasSniparaTools ? "snipara_settings" : hasLegacyTools ? "rlm_settings" : "snipara_settings";
2100
2357
  const settingsResponse = await callHostedMcp(answers, "tools/call", {
2101
- name: "snipara_settings",
2358
+ name: settingsTool,
2102
2359
  arguments: {}
2103
2360
  });
2104
2361
  if (settingsResponse.ok && !settingsResponse.data.error) {
2105
2362
  checks.push({
2106
2363
  name: "hosted_settings",
2107
2364
  status: "pass",
2108
- detail: "snipara_settings tool call succeeded"
2365
+ detail: `${settingsTool} tool call succeeded`
2109
2366
  });
2110
2367
  } else {
2111
2368
  checks.push({
@@ -2126,6 +2383,15 @@ async function testHostedConnection(answers) {
2126
2383
  ];
2127
2384
  }
2128
2385
  }
2386
+ function extractMcpToolNames(data) {
2387
+ const tools = data.result?.tools;
2388
+ if (!Array.isArray(tools)) {
2389
+ return [];
2390
+ }
2391
+ return tools.map(
2392
+ (tool) => tool && typeof tool === "object" && "name" in tool ? String(tool.name || "") : ""
2393
+ ).filter(Boolean);
2394
+ }
2129
2395
  async function callHostedMcp(answers, method, params) {
2130
2396
  const response = await fetch(`${API_URL}/mcp/${answers.projectSlug}`, {
2131
2397
  method: "POST",
@@ -2177,6 +2443,39 @@ function describeJsonRpcError(data) {
2177
2443
  const code = data.error.code !== void 0 ? ` ${data.error.code}` : "";
2178
2444
  return `JSON-RPC error${code}: ${data.error.message || "unknown error"}`;
2179
2445
  }
2446
+ function getCheck(report, name) {
2447
+ return report.checks.find((check) => check.name === name);
2448
+ }
2449
+ function formatCheckStatus(check) {
2450
+ if (!check) {
2451
+ return "not checked";
2452
+ }
2453
+ if (check.status === "pass") {
2454
+ return `OK - ${check.detail}`;
2455
+ }
2456
+ if (check.status === "fail") {
2457
+ return `FAILED - ${check.detail}`;
2458
+ }
2459
+ return `NEEDS ATTENTION - ${check.detail}`;
2460
+ }
2461
+ function printSetupVerdict(answers, report) {
2462
+ const hostedConnection = getCheck(report, "hosted_connection");
2463
+ const toolAliases = getCheck(report, "hosted_tool_aliases");
2464
+ const claudeTools = getCheck(report, "claude_tools_visible");
2465
+ console.log(chalk.bold("\nSetup verdict:\n"));
2466
+ console.log(` Config written: ${answers.profile !== "runtime-only" ? "OK" : "not requested"}`);
2467
+ console.log(` Hosted MCP: ${formatCheckStatus(hostedConnection)}`);
2468
+ if (toolAliases) {
2469
+ console.log(` Tool aliases: ${formatCheckStatus(toolAliases)}`);
2470
+ }
2471
+ if (answers.aiClient === "claude-code" || answers.aiClient === "claude-desktop") {
2472
+ console.log(
2473
+ ` Claude loaded MCP: ${claudeTools ? formatCheckStatus(claudeTools) : "not verified"}`
2474
+ );
2475
+ } else {
2476
+ console.log(" AI client loaded MCP: not verifiable from this terminal");
2477
+ }
2478
+ }
2180
2479
  function printSuccess(answers, report) {
2181
2480
  console.log(
2182
2481
  chalk.green(`
@@ -2189,6 +2488,7 @@ function printSuccess(answers, report) {
2189
2488
  );
2190
2489
  console.log(chalk.bold("Installed profile:\n"));
2191
2490
  console.log(` ${chalk.green("\u2713")} ${answers.profile}`);
2491
+ printSetupVerdict(answers, report);
2192
2492
  console.log(chalk.bold("\nWhat was installed:\n"));
2193
2493
  if (answers.installMcp) {
2194
2494
  console.log(
@@ -2226,6 +2526,20 @@ function printSuccess(answers, report) {
2226
2526
  nextStep += 1;
2227
2527
  };
2228
2528
  printNextStep("Restart your AI client if you created or changed hosted MCP config.");
2529
+ if (answers.aiClient === "claude-code") {
2530
+ printNextStep(
2531
+ "For Claude Code, confirm Snipara appears in Settings -> MCP or ToolSearch; local files alone do not prove Claude loaded the server."
2532
+ );
2533
+ printNextStep("If Snipara tools are missing, run: npx create-snipara doctor --client claude");
2534
+ }
2535
+ if (answers.aiClient === "claude-desktop") {
2536
+ printNextStep(
2537
+ "For Claude Desktop, confirm the Snipara server appears in the desktop MCP settings after restart."
2538
+ );
2539
+ printNextStep(
2540
+ "If Snipara tools are missing, run: npx create-snipara doctor --client claude-desktop"
2541
+ );
2542
+ }
2229
2543
  if (answers.aiClient === "codex") {
2230
2544
  printNextStep(
2231
2545
  "For Codex, merge .snipara/templates/codex-config.toml into ~/.codex/config.toml if the app does not read project .codex/config.toml."
@@ -2291,8 +2605,16 @@ function commandExists(command) {
2291
2605
  return false;
2292
2606
  }
2293
2607
  }
2608
+ function getClaudeConfigPaths() {
2609
+ const desktopDir = process.platform === "darwin" ? path.join(homedir(), "Library", "Application Support", "Claude") : process.platform === "win32" ? path.join(homedir(), "AppData", "Roaming", "Claude") : path.join(homedir(), ".config", "claude");
2610
+ return {
2611
+ codeSettingsPath: path.join(homedir(), ".claude", "settings.json"),
2612
+ desktopConfigPath: path.join(desktopDir, "claude_desktop_config.json")
2613
+ };
2614
+ }
2294
2615
  function detectEnvironment() {
2295
2616
  const existingConfig = getExistingConfig();
2617
+ const claudePaths = getClaudeConfigPaths();
2296
2618
  return {
2297
2619
  cwd: process.cwd(),
2298
2620
  projectSlug: detectProjectSlug(),
@@ -2308,6 +2630,8 @@ function detectEnvironment() {
2308
2630
  codexConfig: fs.existsSync(path.join(process.cwd(), ".codex", "config.toml")),
2309
2631
  cursorRule: fs.existsSync(path.join(process.cwd(), ".cursor", "rules", "snipara.mdc")),
2310
2632
  claudeDir: fs.existsSync(path.join(homedir(), ".claude")),
2633
+ claudeSettings: fs.existsSync(claudePaths.codeSettingsPath),
2634
+ claudeDesktopConfig: fs.existsSync(claudePaths.desktopConfigPath),
2311
2635
  cursorDir: fs.existsSync(path.join(homedir(), ".cursor")),
2312
2636
  sniparaCompanionPack: fs.existsSync(path.join(process.cwd(), ".snipara", "companion")),
2313
2637
  sniparaTemplates: fs.existsSync(path.join(process.cwd(), ".snipara", "templates"))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snipara",
3
- "version": "1.3.21",
3
+ "version": "1.3.23",
4
4
  "description": "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",