create-snipara 1.3.25 → 1.3.27
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/dist/index.js +250 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { homedir } from "os";
|
|
|
13
13
|
// package.json
|
|
14
14
|
var package_default = {
|
|
15
15
|
name: "create-snipara",
|
|
16
|
-
version: "1.3.
|
|
16
|
+
version: "1.3.27",
|
|
17
17
|
description: "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
|
|
18
18
|
main: "dist/index.js",
|
|
19
19
|
type: "module",
|
|
@@ -185,7 +185,7 @@ function buildProgram() {
|
|
|
185
185
|
printBanner();
|
|
186
186
|
await runRepair(options);
|
|
187
187
|
});
|
|
188
|
-
program.command("upgrade").description("Upgrade installed Snipara local components").option("--with-hooks", "Refresh hooks after upgrade").option("--with-runtime", "Upgrade Snipara Sandbox even if not detected").option("--with-mcp", "Upgrade snipara-mcp even if not detected").option("--with-companion", "Upgrade companion even if not detected").option("--with-orchestrator", "Upgrade snipara-orchestrator even if not detected").action(async (options) => {
|
|
188
|
+
program.command("upgrade").description("Upgrade installed Snipara local components").option("--slug <slug>", "Project slug override").option("--with-hooks", "Refresh hooks after upgrade").option("--with-runtime", "Upgrade Snipara Sandbox even if not detected").option("--with-mcp", "Upgrade snipara-mcp even if not detected").option("--with-companion", "Upgrade companion even if not detected").option("--with-orchestrator", "Upgrade snipara-orchestrator even if not detected").action(async (options) => {
|
|
189
189
|
printBanner();
|
|
190
190
|
await runUpgrade(options);
|
|
191
191
|
});
|
|
@@ -286,6 +286,7 @@ async function runInit(options) {
|
|
|
286
286
|
if (answers.profile !== "runtime-only" || answers.installRuntime) {
|
|
287
287
|
await createMcpConfig(answers);
|
|
288
288
|
}
|
|
289
|
+
await createProjectBinding(answers);
|
|
289
290
|
await createAgentTemplates(answers);
|
|
290
291
|
if (answers.installHooks && answers.aiClient === "claude-code") {
|
|
291
292
|
await installClaudeCodeHooks(answers);
|
|
@@ -331,6 +332,7 @@ async function runRepair(options) {
|
|
|
331
332
|
await installNodeCompanion(true);
|
|
332
333
|
}
|
|
333
334
|
await createMcpConfig(answers);
|
|
335
|
+
await createProjectBinding(answers);
|
|
334
336
|
await createAgentTemplates(answers);
|
|
335
337
|
await updateEnvFiles(answers);
|
|
336
338
|
await createCompanionConfig(answers);
|
|
@@ -361,6 +363,7 @@ async function runUpgrade(options) {
|
|
|
361
363
|
if (options.withMcp || options.withRuntime || detection.files.mcpConfig) {
|
|
362
364
|
await createMcpConfig(upgradedAnswers);
|
|
363
365
|
}
|
|
366
|
+
await createProjectBinding(upgradedAnswers);
|
|
364
367
|
await createAgentTemplates(upgradedAnswers);
|
|
365
368
|
await createCompanionConfig(upgradedAnswers);
|
|
366
369
|
if (options.withHooks || answers.installHooks) {
|
|
@@ -385,7 +388,7 @@ async function runPrintConfig(options) {
|
|
|
385
388
|
repository: githubRepo,
|
|
386
389
|
answerPacks: githubAnswerPacks
|
|
387
390
|
},
|
|
388
|
-
inferredSetup: answers
|
|
391
|
+
inferredSetup: redactSetupAnswers(answers)
|
|
389
392
|
},
|
|
390
393
|
null,
|
|
391
394
|
2
|
|
@@ -395,6 +398,7 @@ async function runPrintConfig(options) {
|
|
|
395
398
|
}
|
|
396
399
|
console.log(chalk.bold("\n\u{1F4E6} Current Setup\n"));
|
|
397
400
|
console.log(` Project slug: ${answers.projectSlug}`);
|
|
401
|
+
console.log(` Project slug source: ${detection.projectSlugSource || "not detected"}`);
|
|
398
402
|
console.log(` Project id: ${answers.projectId || "not-set"}`);
|
|
399
403
|
console.log(` Profile: ${answers.profile}`);
|
|
400
404
|
console.log(` AI client: ${answers.aiClient}`);
|
|
@@ -407,11 +411,21 @@ async function runPrintConfig(options) {
|
|
|
407
411
|
console.log(` Snipara Orchestrator: ${answers.installOrchestrator ? "enabled" : "disabled"}`);
|
|
408
412
|
console.log(` Hooks: ${answers.installHooks ? "enabled" : "disabled"}`);
|
|
409
413
|
console.log(` GitHub repo: ${githubRepo ? githubRepo.fullName : "not detected"}`);
|
|
414
|
+
if (detection.detectedRepoSlug && detection.detectedRepoSlug !== answers.projectSlug) {
|
|
415
|
+
console.log(` Git repo slug: ${detection.detectedRepoSlug}`);
|
|
416
|
+
}
|
|
410
417
|
console.log(` ${GITHUB_ANSWER_PACKS_LABEL}: ${githubAnswerPacks}`);
|
|
411
418
|
console.log(` Existing .mcp.json: ${detection.files.mcpConfig ? "yes" : "no"}`);
|
|
412
419
|
console.log(` Companion pack: ${detection.files.sniparaCompanionPack ? "yes" : "no"}`);
|
|
413
420
|
console.log("");
|
|
414
421
|
}
|
|
422
|
+
function redactSetupAnswers(answers) {
|
|
423
|
+
return {
|
|
424
|
+
...answers,
|
|
425
|
+
apiKey: answers.apiKey.includes("YOUR_") ? answers.apiKey : "[redacted]",
|
|
426
|
+
llmApiKey: answers.llmApiKey ? "[redacted]" : void 0
|
|
427
|
+
};
|
|
428
|
+
}
|
|
415
429
|
function printBanner() {
|
|
416
430
|
console.log(
|
|
417
431
|
chalk.cyan(`
|
|
@@ -431,10 +445,11 @@ function printBanner() {
|
|
|
431
445
|
}
|
|
432
446
|
async function gatherConfiguration(options) {
|
|
433
447
|
console.log(chalk.bold("\n\u{1F4CB} Project Configuration\n"));
|
|
434
|
-
let detectedSlug = detectProjectSlug();
|
|
435
|
-
let detectedProjectId = "";
|
|
436
448
|
const detectedClient = detectAiClient();
|
|
437
449
|
const existingConfig = getExistingConfig();
|
|
450
|
+
const projectSlugResolution = resolveProjectSlug(existingConfig);
|
|
451
|
+
let detectedSlug = projectSlugResolution.slug;
|
|
452
|
+
let detectedProjectId = "";
|
|
438
453
|
const detectedGitRepo = detectGitRepository();
|
|
439
454
|
const showAdvancedPrompts = Boolean(options.advanced);
|
|
440
455
|
if (options.github && !detectedGitRepo) {
|
|
@@ -446,7 +461,6 @@ async function gatherConfiguration(options) {
|
|
|
446
461
|
}
|
|
447
462
|
if (existingConfig) {
|
|
448
463
|
console.log(chalk.dim(" Found existing .mcp.json configuration"));
|
|
449
|
-
detectedSlug = existingConfig.slug || detectedSlug;
|
|
450
464
|
detectedProjectId = existingConfig.projectId || "";
|
|
451
465
|
}
|
|
452
466
|
const initialProfile = resolveRequestedProfile(options);
|
|
@@ -1072,6 +1086,19 @@ async function createMcpConfig(answers) {
|
|
|
1072
1086
|
await createClaudeDesktopConfig(answers);
|
|
1073
1087
|
}
|
|
1074
1088
|
}
|
|
1089
|
+
async function createProjectBinding(answers) {
|
|
1090
|
+
if (answers.profile === "runtime-only") {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const sniparaDir = path.join(process.cwd(), ".snipara");
|
|
1094
|
+
const projectBindingPath = path.join(sniparaDir, "project");
|
|
1095
|
+
const projectBinding = `${answers.projectSlug}
|
|
1096
|
+
`;
|
|
1097
|
+
const action = fs.existsSync(projectBindingPath) ? "Updated" : "Created";
|
|
1098
|
+
fs.mkdirSync(sniparaDir, { recursive: true });
|
|
1099
|
+
fs.writeFileSync(projectBindingPath, projectBinding);
|
|
1100
|
+
console.log(chalk.green(` \u2713 ${action} .snipara/project`));
|
|
1101
|
+
}
|
|
1075
1102
|
async function createAgentTemplates(answers) {
|
|
1076
1103
|
if (answers.profile === "runtime-only") {
|
|
1077
1104
|
return;
|
|
@@ -1607,20 +1634,44 @@ async function createCompanionConfig(answers) {
|
|
|
1607
1634
|
}
|
|
1608
1635
|
const configDir = path.join(process.cwd(), ".snipara", "companion");
|
|
1609
1636
|
const configPath = path.join(configDir, "config.json");
|
|
1637
|
+
const globalConfigPath = getGlobalCompanionConfigPath();
|
|
1610
1638
|
fs.mkdirSync(configDir, { recursive: true });
|
|
1611
|
-
|
|
1639
|
+
fs.mkdirSync(path.dirname(globalConfigPath), { recursive: true });
|
|
1640
|
+
const existingGlobalConfig = readCompanionConfigFile(globalConfigPath);
|
|
1641
|
+
const existingWorkspaceConfig = readCompanionConfigFile(configPath);
|
|
1642
|
+
const globalConfig = {
|
|
1643
|
+
...existingGlobalConfig,
|
|
1612
1644
|
apiKey: answers.apiKey,
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1645
|
+
apiUrl: API_URL
|
|
1646
|
+
};
|
|
1647
|
+
const workspaceConfig = {
|
|
1648
|
+
...existingWorkspaceConfig,
|
|
1649
|
+
projectId: answers.projectId || (typeof existingWorkspaceConfig.projectId === "string" && existingWorkspaceConfig.projectId ? existingWorkspaceConfig.projectId : answers.projectSlug),
|
|
1650
|
+
sessionId: typeof existingWorkspaceConfig.sessionId === "string" && existingWorkspaceConfig.sessionId ? existingWorkspaceConfig.sessionId : `sess_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
|
1616
1651
|
};
|
|
1617
|
-
fs.writeFileSync(
|
|
1652
|
+
fs.writeFileSync(globalConfigPath, JSON.stringify(globalConfig, null, 2) + "\n");
|
|
1653
|
+
fs.writeFileSync(configPath, JSON.stringify(workspaceConfig, null, 2) + "\n");
|
|
1618
1654
|
ensureGitignoreEntry(
|
|
1619
1655
|
".snipara/companion/config.json",
|
|
1620
|
-
"# Snipara local companion config
|
|
1656
|
+
"# Snipara local companion workspace config"
|
|
1621
1657
|
);
|
|
1658
|
+
console.log(chalk.green(" \u2713 Configured global companion auth"));
|
|
1622
1659
|
console.log(chalk.green(" \u2713 Configured local companion workspace"));
|
|
1623
1660
|
}
|
|
1661
|
+
function getGlobalCompanionConfigPath() {
|
|
1662
|
+
return path.join(homedir(), ".rlmsaas", "config.json");
|
|
1663
|
+
}
|
|
1664
|
+
function readCompanionConfigFile(filePath) {
|
|
1665
|
+
if (!fs.existsSync(filePath)) {
|
|
1666
|
+
return {};
|
|
1667
|
+
}
|
|
1668
|
+
try {
|
|
1669
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
1670
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
1671
|
+
} catch {
|
|
1672
|
+
return {};
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1624
1675
|
function getClientGuidance(client) {
|
|
1625
1676
|
if (client === "claude-code") {
|
|
1626
1677
|
return [
|
|
@@ -2230,6 +2281,16 @@ async function runValidation(answers, includeHostedCheck, options = {}) {
|
|
|
2230
2281
|
status: answers.projectSlug ? "pass" : "fail",
|
|
2231
2282
|
detail: answers.projectSlug || "Project slug missing"
|
|
2232
2283
|
});
|
|
2284
|
+
const configuredSlugSignals = collectConfiguredProjectSlugSignals(detection.existingConfig);
|
|
2285
|
+
const presentSlugSignals = configuredSlugSignals.filter((signal) => signal.slug);
|
|
2286
|
+
const uniqueConfiguredSlugs = new Set(presentSlugSignals.map((signal) => signal.slug));
|
|
2287
|
+
if (uniqueConfiguredSlugs.size > 1) {
|
|
2288
|
+
checks.push({
|
|
2289
|
+
name: "project_slug_consistency",
|
|
2290
|
+
status: "warn",
|
|
2291
|
+
detail: `Configured slug mismatch: ${presentSlugSignals.map((signal) => `${signal.source}=${signal.slug}`).join(", ")}`
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2233
2294
|
checks.push({
|
|
2234
2295
|
name: "target_client",
|
|
2235
2296
|
status: "pass",
|
|
@@ -2315,9 +2376,11 @@ function writeDoctorReport(report) {
|
|
|
2315
2376
|
function printDoctorReport(detection, report) {
|
|
2316
2377
|
const targetClient = report.checks.find((check) => check.name === "target_client")?.detail || detection.aiClient;
|
|
2317
2378
|
console.log(chalk.bold("\n\u{1FA7A} Snipara Doctor\n"));
|
|
2318
|
-
console.log(
|
|
2319
|
-
|
|
2320
|
-
)
|
|
2379
|
+
console.log(` Project slug: ${detection.projectSlug || "unknown"}`);
|
|
2380
|
+
console.log(` Project slug source: ${detection.projectSlugSource || "not detected"}`);
|
|
2381
|
+
if (detection.detectedRepoSlug && detection.detectedRepoSlug !== detection.projectSlug) {
|
|
2382
|
+
console.log(` Git repo slug: ${detection.detectedRepoSlug}`);
|
|
2383
|
+
}
|
|
2321
2384
|
console.log(` AI client: ${targetClient}`);
|
|
2322
2385
|
console.log(` In git repo: ${detection.inGitRepo ? "yes" : "no"}`);
|
|
2323
2386
|
console.log(` Profile: ${report.profile}`);
|
|
@@ -2629,10 +2692,13 @@ function getClaudeConfigPaths() {
|
|
|
2629
2692
|
}
|
|
2630
2693
|
function detectEnvironment() {
|
|
2631
2694
|
const existingConfig = getExistingConfig();
|
|
2695
|
+
const projectSlugResolution = resolveProjectSlug(existingConfig);
|
|
2632
2696
|
const claudePaths = getClaudeConfigPaths();
|
|
2633
2697
|
return {
|
|
2634
2698
|
cwd: process.cwd(),
|
|
2635
|
-
projectSlug:
|
|
2699
|
+
projectSlug: projectSlugResolution.slug,
|
|
2700
|
+
projectSlugSource: projectSlugResolution.source,
|
|
2701
|
+
detectedRepoSlug: detectRepoProjectSlug(),
|
|
2636
2702
|
aiClient: detectAiClient(),
|
|
2637
2703
|
inGitRepo: isGitRepo(),
|
|
2638
2704
|
files: {
|
|
@@ -2644,6 +2710,7 @@ function detectEnvironment() {
|
|
|
2644
2710
|
claudeMd: fs.existsSync(path.join(process.cwd(), "CLAUDE.md")),
|
|
2645
2711
|
codexConfig: fs.existsSync(path.join(process.cwd(), ".codex", "config.toml")),
|
|
2646
2712
|
cursorRule: fs.existsSync(path.join(process.cwd(), ".cursor", "rules", "snipara.mdc")),
|
|
2713
|
+
sniparaProject: fs.existsSync(path.join(process.cwd(), ".snipara", "project")),
|
|
2647
2714
|
claudeDir: fs.existsSync(path.join(homedir(), ".claude")),
|
|
2648
2715
|
claudeSettings: fs.existsSync(claudePaths.codeSettingsPath),
|
|
2649
2716
|
claudeDesktopConfig: fs.existsSync(claudePaths.desktopConfigPath),
|
|
@@ -2683,7 +2750,7 @@ function loadExistingAnswers(options) {
|
|
|
2683
2750
|
const apiKey = providedApiKey || envApiKey || "YOUR_API_KEY_HERE";
|
|
2684
2751
|
const apiKeyType = effectiveProfile === "runtime-only" ? "none" : providedApiKey || envApiKey ? "api" : "none";
|
|
2685
2752
|
return buildAnswersFromProfile({
|
|
2686
|
-
projectSlug: options.slug || detection.
|
|
2753
|
+
projectSlug: options.slug || detection.projectSlug || "my-project",
|
|
2687
2754
|
projectId: options.projectId || detection.existingConfig.projectId || "",
|
|
2688
2755
|
apiKey,
|
|
2689
2756
|
apiKeyType,
|
|
@@ -2714,17 +2781,157 @@ function resolveMaintenanceProfile(detection, explicitProfile) {
|
|
|
2714
2781
|
function readApiKeyFromEnvFiles() {
|
|
2715
2782
|
const candidates = [".env.local", ".env"];
|
|
2716
2783
|
for (const file of candidates) {
|
|
2717
|
-
const
|
|
2718
|
-
if (
|
|
2784
|
+
const value = readEnvValueFromFile(file, ["SNIPARA_API_KEY", "RLM_API_KEY"]);
|
|
2785
|
+
if (value) {
|
|
2786
|
+
return value;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
return null;
|
|
2790
|
+
}
|
|
2791
|
+
function resolveProjectSlug(existingConfig = getExistingConfig()) {
|
|
2792
|
+
const configuredSignals = collectConfiguredProjectSlugSignals(existingConfig);
|
|
2793
|
+
const signals = [
|
|
2794
|
+
...configuredSignals,
|
|
2795
|
+
{ source: "git remote", slug: detectRepoProjectSlug() },
|
|
2796
|
+
{ source: "working directory", slug: detectDirectoryProjectSlug() }
|
|
2797
|
+
];
|
|
2798
|
+
const selected = signals.find((signal) => signal.slug);
|
|
2799
|
+
return {
|
|
2800
|
+
slug: selected?.slug || null,
|
|
2801
|
+
source: selected?.source || null,
|
|
2802
|
+
signals
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
function collectConfiguredProjectSlugSignals(existingConfig = getExistingConfig()) {
|
|
2806
|
+
return [
|
|
2807
|
+
{ source: ".mcp.json", slug: normalizeSlugCandidate(existingConfig?.slug) },
|
|
2808
|
+
{ source: ".snipara/project", slug: readProjectSlugFromProjectBinding() },
|
|
2809
|
+
{
|
|
2810
|
+
source: ".codex/config.toml",
|
|
2811
|
+
slug: readProjectSlugFromTextFile(path.join(process.cwd(), ".codex", "config.toml"))
|
|
2812
|
+
},
|
|
2813
|
+
{
|
|
2814
|
+
source: ".snipara/templates/codex-config.toml",
|
|
2815
|
+
slug: readProjectSlugFromTextFile(
|
|
2816
|
+
path.join(process.cwd(), ".snipara", "templates", "codex-config.toml")
|
|
2817
|
+
)
|
|
2818
|
+
},
|
|
2819
|
+
{ source: ".env.local", slug: readProjectSlugFromEnvFile(".env.local") },
|
|
2820
|
+
{ source: ".env", slug: readProjectSlugFromEnvFile(".env") }
|
|
2821
|
+
];
|
|
2822
|
+
}
|
|
2823
|
+
function normalizeSlugCandidate(value) {
|
|
2824
|
+
if (!value) {
|
|
2825
|
+
return null;
|
|
2826
|
+
}
|
|
2827
|
+
const trimmed = value.trim().replace(/^["']|["']$/g, "");
|
|
2828
|
+
if (!trimmed || /^your_/i.test(trimmed) || /^YOUR_/.test(trimmed) || trimmed.includes("<") || trimmed.includes(">")) {
|
|
2829
|
+
return null;
|
|
2830
|
+
}
|
|
2831
|
+
return trimmed;
|
|
2832
|
+
}
|
|
2833
|
+
function slugifyFallback(value) {
|
|
2834
|
+
const normalized = normalizeSlugCandidate(value);
|
|
2835
|
+
if (!normalized) {
|
|
2836
|
+
return null;
|
|
2837
|
+
}
|
|
2838
|
+
const slug = normalized.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2839
|
+
return slug || null;
|
|
2840
|
+
}
|
|
2841
|
+
function extractSlugFromMcpEndpoint(value) {
|
|
2842
|
+
const endpoint = normalizeSlugCandidate(value);
|
|
2843
|
+
if (!endpoint) {
|
|
2844
|
+
return null;
|
|
2845
|
+
}
|
|
2846
|
+
const match = endpoint.match(/\/mcp\/([^/?#\s"']+)/);
|
|
2847
|
+
return normalizeSlugCandidate(match?.[1] ? decodeURIComponent(match[1]) : null);
|
|
2848
|
+
}
|
|
2849
|
+
function extractSlugFromText(content) {
|
|
2850
|
+
const match = content.match(/\/mcp\/([^/?#\s"'`]+)/);
|
|
2851
|
+
return normalizeSlugCandidate(match?.[1] ? decodeURIComponent(match[1]) : null);
|
|
2852
|
+
}
|
|
2853
|
+
function readProjectSlugFromTextFile(filePath) {
|
|
2854
|
+
if (!fs.existsSync(filePath)) {
|
|
2855
|
+
return null;
|
|
2856
|
+
}
|
|
2857
|
+
try {
|
|
2858
|
+
return extractSlugFromText(fs.readFileSync(filePath, "utf-8"));
|
|
2859
|
+
} catch {
|
|
2860
|
+
return null;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
function readProjectSlugFromProjectBinding() {
|
|
2864
|
+
const filePath = path.join(process.cwd(), ".snipara", "project");
|
|
2865
|
+
if (!fs.existsSync(filePath)) {
|
|
2866
|
+
return null;
|
|
2867
|
+
}
|
|
2868
|
+
try {
|
|
2869
|
+
const content = fs.readFileSync(filePath, "utf-8").trim();
|
|
2870
|
+
if (!content) {
|
|
2871
|
+
return null;
|
|
2872
|
+
}
|
|
2873
|
+
if (content.startsWith("{")) {
|
|
2874
|
+
const parsed = JSON.parse(content);
|
|
2875
|
+
return normalizeSlugCandidate(parsed.slug || parsed.projectSlug || parsed.project_slug);
|
|
2876
|
+
}
|
|
2877
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2878
|
+
const trimmed = line.trim();
|
|
2879
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
2880
|
+
continue;
|
|
2881
|
+
}
|
|
2882
|
+
const keyed = trimmed.match(/^(?:slug|project|projectSlug|project_slug)\s*[:=]\s*(.+)$/i);
|
|
2883
|
+
if (keyed?.[1]) {
|
|
2884
|
+
return normalizeSlugCandidate(stripInlineEnvComment(keyed[1]));
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
const firstValue = content.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !line.startsWith("#"));
|
|
2888
|
+
return normalizeSlugCandidate(firstValue);
|
|
2889
|
+
} catch {
|
|
2890
|
+
return null;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
function readProjectSlugFromEnvFile(file) {
|
|
2894
|
+
const explicitSlug = readEnvValueFromFile(file, ["SNIPARA_PROJECT_SLUG", "RLM_PROJECT_SLUG"]);
|
|
2895
|
+
if (explicitSlug) {
|
|
2896
|
+
return explicitSlug;
|
|
2897
|
+
}
|
|
2898
|
+
const legacySlug = readEnvValueFromFile(file, ["SNIPARA_PROJECT_ID", "RLM_PROJECT_ID"]);
|
|
2899
|
+
if (legacySlug && /^[a-z0-9][a-z0-9-]*$/i.test(legacySlug)) {
|
|
2900
|
+
return legacySlug;
|
|
2901
|
+
}
|
|
2902
|
+
return null;
|
|
2903
|
+
}
|
|
2904
|
+
function readEnvValueFromFile(file, names) {
|
|
2905
|
+
const filePath = path.join(process.cwd(), file);
|
|
2906
|
+
if (!fs.existsSync(filePath)) {
|
|
2907
|
+
return null;
|
|
2908
|
+
}
|
|
2909
|
+
try {
|
|
2719
2910
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
2720
|
-
const
|
|
2721
|
-
|
|
2722
|
-
|
|
2911
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2912
|
+
const match = line.trim().match(/^(?:export\s+)?([A-Z0-9_]+)\s*=\s*(.*)$/);
|
|
2913
|
+
if (!match || !names.includes(match[1])) {
|
|
2914
|
+
continue;
|
|
2915
|
+
}
|
|
2916
|
+
const value = normalizeSlugCandidate(stripInlineEnvComment(match[2]));
|
|
2917
|
+
if (value) {
|
|
2918
|
+
return value;
|
|
2919
|
+
}
|
|
2723
2920
|
}
|
|
2921
|
+
} catch {
|
|
2922
|
+
return null;
|
|
2724
2923
|
}
|
|
2725
2924
|
return null;
|
|
2726
2925
|
}
|
|
2727
|
-
function
|
|
2926
|
+
function stripInlineEnvComment(value) {
|
|
2927
|
+
const trimmed = value.trim();
|
|
2928
|
+
const quoted = trimmed.match(/^(['"])(.*)\1$/);
|
|
2929
|
+
if (quoted) {
|
|
2930
|
+
return quoted[2].trim();
|
|
2931
|
+
}
|
|
2932
|
+
return trimmed.replace(/\s+#.*$/, "").trim();
|
|
2933
|
+
}
|
|
2934
|
+
function detectRepoProjectSlug() {
|
|
2728
2935
|
try {
|
|
2729
2936
|
const remote = execSync("git remote get-url origin", {
|
|
2730
2937
|
encoding: "utf-8",
|
|
@@ -2732,11 +2939,14 @@ function detectProjectSlug() {
|
|
|
2732
2939
|
}).trim();
|
|
2733
2940
|
const match = remote.match(/[/:]([^/]+?)(?:\.git)?$/);
|
|
2734
2941
|
if (match) {
|
|
2735
|
-
return match[1]
|
|
2942
|
+
return slugifyFallback(match[1]);
|
|
2736
2943
|
}
|
|
2737
2944
|
} catch {
|
|
2738
2945
|
}
|
|
2739
|
-
return
|
|
2946
|
+
return null;
|
|
2947
|
+
}
|
|
2948
|
+
function detectDirectoryProjectSlug() {
|
|
2949
|
+
return slugifyFallback(path.basename(process.cwd()));
|
|
2740
2950
|
}
|
|
2741
2951
|
function detectGitRepository() {
|
|
2742
2952
|
try {
|
|
@@ -2854,22 +3064,33 @@ function getExistingConfig() {
|
|
|
2854
3064
|
const config = JSON.parse(fs.readFileSync(mcpConfigPath, "utf-8"));
|
|
2855
3065
|
const sniparaServer = config.mcpServers?.snipara;
|
|
2856
3066
|
const runtimeServer = config.mcpServers?.[SANDBOX_SERVER_NAME] || config.mcpServers?.[LEGACY_RUNTIME_SERVER_NAME];
|
|
2857
|
-
const match = sniparaServer?.url?.match(/\/mcp\/([^/]+)$/);
|
|
2858
3067
|
return {
|
|
2859
|
-
slug:
|
|
2860
|
-
projectId:
|
|
3068
|
+
slug: extractSlugFromMcpEndpoint(sniparaServer?.url) || void 0,
|
|
3069
|
+
projectId: readExistingCompanionProjectId(),
|
|
2861
3070
|
hasSniparaServer: Boolean(sniparaServer),
|
|
2862
3071
|
hasRuntimeServer: Boolean(runtimeServer)
|
|
2863
3072
|
};
|
|
2864
3073
|
} catch {
|
|
2865
3074
|
return {
|
|
2866
3075
|
slug: void 0,
|
|
2867
|
-
projectId:
|
|
3076
|
+
projectId: readExistingCompanionProjectId(),
|
|
2868
3077
|
hasSniparaServer: false,
|
|
2869
3078
|
hasRuntimeServer: false
|
|
2870
3079
|
};
|
|
2871
3080
|
}
|
|
2872
3081
|
}
|
|
3082
|
+
function readExistingCompanionProjectId() {
|
|
3083
|
+
const configPath = path.join(process.cwd(), ".snipara", "companion", "config.json");
|
|
3084
|
+
if (!fs.existsSync(configPath)) {
|
|
3085
|
+
return void 0;
|
|
3086
|
+
}
|
|
3087
|
+
try {
|
|
3088
|
+
const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
3089
|
+
return typeof parsed.projectId === "string" && parsed.projectId ? parsed.projectId : void 0;
|
|
3090
|
+
} catch {
|
|
3091
|
+
return void 0;
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
2873
3094
|
main().catch((error) => {
|
|
2874
3095
|
console.error(chalk.red("Error:"), error.message);
|
|
2875
3096
|
process.exit(1);
|
package/package.json
CHANGED