@01.software/init 0.3.0 → 0.4.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/dist/ai-docs.js +12 -0
- package/dist/ai-docs.js.map +1 -0
- package/dist/chunk-OEAQV63E.js +120 -0
- package/dist/chunk-OEAQV63E.js.map +1 -0
- package/dist/chunk-SRLZ5OIV.js +163 -0
- package/dist/chunk-SRLZ5OIV.js.map +1 -0
- package/dist/index.js +187 -348
- package/dist/index.js.map +1 -1
- package/dist/templates.js +22 -0
- package/dist/templates.js.map +1 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
fetchTenantContext,
|
|
4
|
+
generateClaudeMd,
|
|
5
|
+
getSkillFiles
|
|
6
|
+
} from "./chunk-SRLZ5OIV.js";
|
|
7
|
+
import {
|
|
8
|
+
CODEX_MCP_SECTION_MARKER,
|
|
9
|
+
getClientTemplate,
|
|
10
|
+
getCodexMcpTomlSection,
|
|
11
|
+
getEnvContent,
|
|
12
|
+
getMcpConfigTemplate,
|
|
13
|
+
getMcpServerEntry,
|
|
14
|
+
getQueryProviderTemplate,
|
|
15
|
+
getServerTemplate
|
|
16
|
+
} from "./chunk-OEAQV63E.js";
|
|
2
17
|
|
|
3
18
|
// src/index.ts
|
|
4
19
|
import pc3 from "picocolors";
|
|
@@ -11,12 +26,13 @@ function detectProject(cwd) {
|
|
|
11
26
|
const hasPackageJson = fs.existsSync(pkgPath);
|
|
12
27
|
let env = "node";
|
|
13
28
|
let hasSdk = false;
|
|
29
|
+
let parseError = false;
|
|
14
30
|
if (hasPackageJson) {
|
|
15
31
|
let pkg;
|
|
16
32
|
try {
|
|
17
33
|
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
18
34
|
} catch {
|
|
19
|
-
return { hasPackageJson:
|
|
35
|
+
return { hasPackageJson: true, parseError: true, env: "node", packageManager: null, hasSdk: false, srcDir: false };
|
|
20
36
|
}
|
|
21
37
|
const deps = {
|
|
22
38
|
...pkg.dependencies || {},
|
|
@@ -52,7 +68,7 @@ function detectProject(cwd) {
|
|
|
52
68
|
packageManager = "npm";
|
|
53
69
|
}
|
|
54
70
|
const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src"));
|
|
55
|
-
return { hasPackageJson, env, packageManager, hasSdk, srcDir };
|
|
71
|
+
return { hasPackageJson, parseError, env, packageManager, hasSdk, srcDir };
|
|
56
72
|
}
|
|
57
73
|
function needsClient(env) {
|
|
58
74
|
return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla";
|
|
@@ -171,7 +187,7 @@ async function promptUser(hasSdk, detectedEnv, detectedPm) {
|
|
|
171
187
|
keyPrompts.push({
|
|
172
188
|
type: "text",
|
|
173
189
|
name: "publishableKey",
|
|
174
|
-
message: "
|
|
190
|
+
message: "Publishable Key (optional, saved to .env)",
|
|
175
191
|
initial: ""
|
|
176
192
|
});
|
|
177
193
|
}
|
|
@@ -187,19 +203,20 @@ async function promptUser(hasSdk, detectedEnv, detectedPm) {
|
|
|
187
203
|
{
|
|
188
204
|
type: "multiselect",
|
|
189
205
|
name: "selectedTools",
|
|
190
|
-
message: "Connect AI tools:",
|
|
206
|
+
message: "Connect AI tools (leave empty to skip):",
|
|
191
207
|
choices: [
|
|
192
208
|
{ title: "Claude Code", description: ".mcp.json + .claude/ docs", value: "claude" },
|
|
193
209
|
{ title: "Cursor", description: ".cursor/mcp.json", value: "cursor" },
|
|
194
210
|
{ title: "VS Code", description: ".vscode/mcp.json", value: "vscode" },
|
|
195
211
|
{ title: "Windsurf", description: "~/.codeium/windsurf/mcp_config.json", value: "windsurf" },
|
|
196
|
-
{ title: "
|
|
212
|
+
{ title: "Codex CLI", description: "~/.codex/config.toml", value: "codex" },
|
|
213
|
+
{ title: "Gemini CLI", description: "~/.gemini/settings.json", value: "gemini" }
|
|
197
214
|
],
|
|
198
|
-
hint: "space to select"
|
|
215
|
+
hint: "space to select, enter to confirm"
|
|
199
216
|
},
|
|
200
217
|
{ onCancel }
|
|
201
218
|
);
|
|
202
|
-
const aiTools = Array.isArray(selectedTools) ? selectedTools
|
|
219
|
+
const aiTools = Array.isArray(selectedTools) ? selectedTools : [];
|
|
203
220
|
let authMethod = "skip";
|
|
204
221
|
let keys = {};
|
|
205
222
|
if (aiTools.length > 0 && env !== "vanilla") {
|
|
@@ -239,103 +256,10 @@ async function promptUser(hasSdk, detectedEnv, detectedPm) {
|
|
|
239
256
|
// src/init.ts
|
|
240
257
|
import fs2 from "fs";
|
|
241
258
|
import path2 from "path";
|
|
259
|
+
import os from "os";
|
|
242
260
|
import { execSync } from "child_process";
|
|
243
261
|
import pc2 from "picocolors";
|
|
244
262
|
|
|
245
|
-
// src/templates.ts
|
|
246
|
-
function getClientTemplate(env, publishableKeyEnvVar) {
|
|
247
|
-
if (env === "nextjs") {
|
|
248
|
-
return `import { createClient } from '@01.software/sdk'
|
|
249
|
-
|
|
250
|
-
export const client = createClient({
|
|
251
|
-
publishableKey: process.env.${publishableKeyEnvVar}!,
|
|
252
|
-
})
|
|
253
|
-
`;
|
|
254
|
-
}
|
|
255
|
-
if (env === "react-cra") {
|
|
256
|
-
return `import { createClient } from '@01.software/sdk'
|
|
257
|
-
|
|
258
|
-
export const client = createClient({
|
|
259
|
-
publishableKey: process.env.${publishableKeyEnvVar}!,
|
|
260
|
-
})
|
|
261
|
-
`;
|
|
262
|
-
}
|
|
263
|
-
if (env === "vanilla") {
|
|
264
|
-
return `import { createClient } from '@01.software/sdk'
|
|
265
|
-
|
|
266
|
-
// Replace 'YOUR_PUBLISHABLE_KEY' with your actual publishable key from the 01.software console
|
|
267
|
-
export const client = createClient({
|
|
268
|
-
publishableKey: 'YOUR_PUBLISHABLE_KEY',
|
|
269
|
-
})
|
|
270
|
-
`;
|
|
271
|
-
}
|
|
272
|
-
return `import { createClient } from '@01.software/sdk'
|
|
273
|
-
|
|
274
|
-
export const client = createClient({
|
|
275
|
-
publishableKey: import.meta.env.${publishableKeyEnvVar},
|
|
276
|
-
})
|
|
277
|
-
`;
|
|
278
|
-
}
|
|
279
|
-
function getQueryProviderTemplate(env) {
|
|
280
|
-
const useClientDirective = env === "nextjs" ? "'use client'\n\n" : "";
|
|
281
|
-
return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query'
|
|
282
|
-
import { client } from './client'
|
|
283
|
-
|
|
284
|
-
export function QueryProvider({ children }: { children: React.ReactNode }) {
|
|
285
|
-
return (
|
|
286
|
-
<QueryClientProvider client={client.queryClient}>
|
|
287
|
-
{children}
|
|
288
|
-
</QueryClientProvider>
|
|
289
|
-
)
|
|
290
|
-
}
|
|
291
|
-
`;
|
|
292
|
-
}
|
|
293
|
-
function getServerTemplate(env, publishableKeyEnvVar, secretKeyEnvVar) {
|
|
294
|
-
if (env === "edge") {
|
|
295
|
-
return `import { createServerClient } from '@01.software/sdk'
|
|
296
|
-
|
|
297
|
-
// Edge runtime: pass your env bindings here
|
|
298
|
-
// e.g. Cloudflare Workers: use env.SOFTWARE_PUBLISHABLE_KEY from the handler context
|
|
299
|
-
// e.g. Vercel Edge: use process.env.${publishableKeyEnvVar}
|
|
300
|
-
export function createEdgeClient(publishableKey: string, secretKey: string) {
|
|
301
|
-
return createServerClient({ publishableKey, secretKey })
|
|
302
|
-
}
|
|
303
|
-
`;
|
|
304
|
-
}
|
|
305
|
-
return `import { createServerClient } from '@01.software/sdk'
|
|
306
|
-
|
|
307
|
-
export const serverClient = createServerClient({
|
|
308
|
-
publishableKey: process.env.${publishableKeyEnvVar}!,
|
|
309
|
-
secretKey: process.env.${secretKeyEnvVar}!,
|
|
310
|
-
})
|
|
311
|
-
`;
|
|
312
|
-
}
|
|
313
|
-
function getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar) {
|
|
314
|
-
let content = `
|
|
315
|
-
# 01.software
|
|
316
|
-
${publishableKeyEnvVar}=${publishableKey}
|
|
317
|
-
`;
|
|
318
|
-
if (secretKeyEnvVar) {
|
|
319
|
-
content += `${secretKeyEnvVar}=${secretKey}
|
|
320
|
-
`;
|
|
321
|
-
}
|
|
322
|
-
return content;
|
|
323
|
-
}
|
|
324
|
-
function getMcpServerEntry(apiKey) {
|
|
325
|
-
return {
|
|
326
|
-
type: "http",
|
|
327
|
-
url: "https://mcp.01.software/mcp",
|
|
328
|
-
headers: { "x-api-key": apiKey }
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
function getMcpConfigTemplate(apiKey) {
|
|
332
|
-
return JSON.stringify(
|
|
333
|
-
{ mcpServers: { "01software": getMcpServerEntry(apiKey) } },
|
|
334
|
-
null,
|
|
335
|
-
2
|
|
336
|
-
) + "\n";
|
|
337
|
-
}
|
|
338
|
-
|
|
339
263
|
// src/browser-auth.ts
|
|
340
264
|
import { randomBytes } from "crypto";
|
|
341
265
|
import { createServer } from "http";
|
|
@@ -349,7 +273,7 @@ function escapeHtml(s) {
|
|
|
349
273
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
350
274
|
}
|
|
351
275
|
function openBrowser(url) {
|
|
352
|
-
const
|
|
276
|
+
const os2 = platform();
|
|
353
277
|
const onError = () => {
|
|
354
278
|
console.log(
|
|
355
279
|
pc.yellow(
|
|
@@ -358,12 +282,12 @@ ${url}`
|
|
|
358
282
|
)
|
|
359
283
|
);
|
|
360
284
|
};
|
|
361
|
-
if (
|
|
285
|
+
if (os2 === "win32") {
|
|
362
286
|
exec(`start "" "${url}"`, (err) => {
|
|
363
287
|
if (err) onError();
|
|
364
288
|
});
|
|
365
289
|
} else {
|
|
366
|
-
const cmd =
|
|
290
|
+
const cmd = os2 === "darwin" ? "open" : "xdg-open";
|
|
367
291
|
execFile(cmd, [url], (err) => {
|
|
368
292
|
if (err) onError();
|
|
369
293
|
});
|
|
@@ -395,6 +319,7 @@ async function startBrowserAuth(options) {
|
|
|
395
319
|
"Access-Control-Allow-Origin": webUrl,
|
|
396
320
|
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
397
321
|
"Access-Control-Allow-Headers": "Content-Type",
|
|
322
|
+
"Access-Control-Allow-Private-Network": "true",
|
|
398
323
|
"Access-Control-Max-Age": "600",
|
|
399
324
|
Vary: "Origin"
|
|
400
325
|
};
|
|
@@ -518,156 +443,6 @@ ${loginUrl}`));
|
|
|
518
443
|
});
|
|
519
444
|
}
|
|
520
445
|
|
|
521
|
-
// src/ai-docs.ts
|
|
522
|
-
function generateClaudeMd(ctx) {
|
|
523
|
-
const featuresSection = ctx.features && ctx.features.length > 0 ? ctx.features.map((f) => `- ${f}`).join("\n") : "- See console";
|
|
524
|
-
const collectionsSection = ctx.collections && ctx.collections.length > 0 ? ctx.collections.join(", ") : "Run `01 schema list`";
|
|
525
|
-
return `# 01.software SDK \u2014 ${ctx.tenantName}
|
|
526
|
-
|
|
527
|
-
## Connection
|
|
528
|
-
- Publishable Key: \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` (env)
|
|
529
|
-
- Secret Key: \`SOFTWARE_SECRET_KEY\` (env)
|
|
530
|
-
- MCP: \`.mcp.json\`
|
|
531
|
-
|
|
532
|
-
## Active Features
|
|
533
|
-
${featuresSection}
|
|
534
|
-
|
|
535
|
-
## Collections
|
|
536
|
-
${collectionsSection}
|
|
537
|
-
|
|
538
|
-
## MCP Quick Reference
|
|
539
|
-
| Tool | Use |
|
|
540
|
-
|------|-----|
|
|
541
|
-
| \`query-collection\` | List/filter documents |
|
|
542
|
-
| \`create-collection\` | Create documents |
|
|
543
|
-
| \`update-field-config\` | Hide unused fields |
|
|
544
|
-
| \`get-tenant-context\` | Show active features & collections |
|
|
545
|
-
|
|
546
|
-
## CLI
|
|
547
|
-
- \`01 query <collection>\` \u2014 query data
|
|
548
|
-
- \`01 schema show <collection>\` \u2014 inspect fields
|
|
549
|
-
- \`01 schema list\` \u2014 list all collections
|
|
550
|
-
|
|
551
|
-
## Initial Setup
|
|
552
|
-
Run \`/01software-field-config\` in Claude Code to configure field visibility for your use case.
|
|
553
|
-
`;
|
|
554
|
-
}
|
|
555
|
-
function getSkillFiles() {
|
|
556
|
-
return [
|
|
557
|
-
{
|
|
558
|
-
dirName: "01software-field-config",
|
|
559
|
-
content: `---
|
|
560
|
-
name: 01software-field-config
|
|
561
|
-
description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP
|
|
562
|
-
disable-model-invocation: true
|
|
563
|
-
---
|
|
564
|
-
|
|
565
|
-
Steps:
|
|
566
|
-
1. Use \`list-configurable-fields\` to see current visibility settings
|
|
567
|
-
2. Identify fields/collections not needed for your use case
|
|
568
|
-
3. Use \`update-field-config\` to hide them
|
|
569
|
-
|
|
570
|
-
Common setups:
|
|
571
|
-
- Blog only: hide \`ecommerce\`, \`customers\`, \`videos\` collections
|
|
572
|
-
- Store: hide \`posts\`, \`documents\`, \`galleries\`, \`canvas\` collections
|
|
573
|
-
- Minimal: hide all except the collections you actively use
|
|
574
|
-
|
|
575
|
-
Ask me: "Show current field config" or "Hide ecommerce fields"
|
|
576
|
-
`
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
dirName: "01software-query",
|
|
580
|
-
content: `---
|
|
581
|
-
name: 01software-query
|
|
582
|
-
description: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples
|
|
583
|
-
---
|
|
584
|
-
|
|
585
|
-
Query collections using the MCP \`query-collection\` tool or CLI.
|
|
586
|
-
|
|
587
|
-
MCP examples:
|
|
588
|
-
- List products: \`query-collection\` with collection="products", limit=10
|
|
589
|
-
- Filter by status: add where={"status":{"equals":"published"}}
|
|
590
|
-
- Sort by date: sort="-createdAt"
|
|
591
|
-
- Paginate: page=2, limit=20
|
|
592
|
-
|
|
593
|
-
CLI examples:
|
|
594
|
-
- \`01 query products --limit 10\`
|
|
595
|
-
- \`01 query orders --where '{"status":{"equals":"paid"}}'\`
|
|
596
|
-
- \`01 schema show products\` \u2014 inspect available fields
|
|
597
|
-
|
|
598
|
-
SDK (server):
|
|
599
|
-
\`\`\`typescript
|
|
600
|
-
const { docs } = await serverClient.collection('products').find({
|
|
601
|
-
where: { status: { equals: 'published' } },
|
|
602
|
-
sort: '-createdAt',
|
|
603
|
-
limit: 10,
|
|
604
|
-
})
|
|
605
|
-
\`\`\`
|
|
606
|
-
`
|
|
607
|
-
},
|
|
608
|
-
{
|
|
609
|
-
dirName: "01software-order-flow",
|
|
610
|
-
content: `---
|
|
611
|
-
name: 01software-order-flow
|
|
612
|
-
description: Order lifecycle reference \u2014 create, pay, fulfill, and return flows for 01.software
|
|
613
|
-
---
|
|
614
|
-
|
|
615
|
-
Complete order flow from creation to fulfillment.
|
|
616
|
-
|
|
617
|
-
States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2192 confirmed
|
|
618
|
-
|
|
619
|
-
1. Create order: \`create-order\` with orderNumber, customerSnapshot, orderProducts, totalAmount
|
|
620
|
-
2. Mark paid: \`update-order\` with status="paid" (after payment gateway confirms)
|
|
621
|
-
3. Fulfill: \`create-fulfillment\` with items and carrier/trackingNumber
|
|
622
|
-
4. Returns: \`create-return\` or \`return-with-refund\` (atomic)
|
|
623
|
-
|
|
624
|
-
Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid
|
|
625
|
-
|
|
626
|
-
CLI: \`01 order create --help\` for full options
|
|
627
|
-
`
|
|
628
|
-
},
|
|
629
|
-
{
|
|
630
|
-
dirName: "01software-schema",
|
|
631
|
-
content: `---
|
|
632
|
-
name: 01software-schema
|
|
633
|
-
description: Inspect 01.software collection schemas and available fields via MCP or CLI
|
|
634
|
-
---
|
|
635
|
-
|
|
636
|
-
Inspect collection schemas to understand available fields.
|
|
637
|
-
|
|
638
|
-
MCP: use \`get-collection-fields\` with collectionSlug
|
|
639
|
-
|
|
640
|
-
CLI:
|
|
641
|
-
- \`01 schema list\` \u2014 all available collections
|
|
642
|
-
- \`01 schema show <collection>\` \u2014 field names, types, required status
|
|
643
|
-
|
|
644
|
-
Common collections: products, orders, customers, posts, documents, images
|
|
645
|
-
Use \`get-tenant-context\` to see which collections are active for this tenant.
|
|
646
|
-
`
|
|
647
|
-
}
|
|
648
|
-
];
|
|
649
|
-
}
|
|
650
|
-
async function fetchTenantContext(publishableKey, secretKey) {
|
|
651
|
-
try {
|
|
652
|
-
const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software";
|
|
653
|
-
const res = await fetch(`${apiUrl}/api/tenants/context`, {
|
|
654
|
-
headers: {
|
|
655
|
-
"X-Publishable-Key": publishableKey,
|
|
656
|
-
Authorization: `Bearer ${secretKey}`
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
if (!res.ok) return null;
|
|
660
|
-
const data = await res.json();
|
|
661
|
-
return {
|
|
662
|
-
tenantName: data.tenant?.name || "",
|
|
663
|
-
features: data.features || [],
|
|
664
|
-
collections: data.collections || []
|
|
665
|
-
};
|
|
666
|
-
} catch {
|
|
667
|
-
return null;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
446
|
// src/init.ts
|
|
672
447
|
var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY";
|
|
673
448
|
async function init(cwd, info, answers) {
|
|
@@ -680,71 +455,55 @@ async function init(cwd, info, answers) {
|
|
|
680
455
|
const wantsReactQuery = needsReactQuery(env);
|
|
681
456
|
const deps = ["@01.software/sdk"];
|
|
682
457
|
if (wantsReactQuery) deps.push("@tanstack/react-query");
|
|
458
|
+
const addCmd = buildAddCmd(packageManager, hasPnpmWorkspace(cwd), deps);
|
|
459
|
+
let installFailed = false;
|
|
683
460
|
console.log(pc2.dim(` Installing ${deps.join(" and ")}...`));
|
|
684
461
|
const wsPatched = packageManager === "pnpm" && patchPnpmWorkspace(cwd);
|
|
685
|
-
const pkgs = deps.join(" ");
|
|
686
|
-
const pnpmFlag = hasPnpmWorkspace(cwd) ? " -w" : "";
|
|
687
|
-
const addCmd = packageManager === "pnpm" ? `pnpm add${pnpmFlag} ${pkgs}` : packageManager === "yarn" ? `yarn add ${pkgs}` : packageManager === "bun" ? `bun add ${pkgs}` : `npm install ${pkgs}`;
|
|
688
462
|
try {
|
|
689
463
|
execSync(addCmd, { cwd, stdio: "pipe" });
|
|
464
|
+
console.log(pc2.green(" Installed"), deps.join(", "));
|
|
690
465
|
} catch (error) {
|
|
466
|
+
installFailed = true;
|
|
691
467
|
const err = error;
|
|
692
468
|
const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error);
|
|
693
|
-
console.log(pc2.
|
|
694
|
-
|
|
695
|
-
|
|
469
|
+
console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding"));
|
|
470
|
+
const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n");
|
|
471
|
+
if (firstLines) console.log(pc2.dim(firstLines));
|
|
472
|
+
console.log(pc2.dim(` Run manually: ${addCmd}`));
|
|
696
473
|
} finally {
|
|
697
474
|
if (wsPatched) restorePnpmWorkspace(cwd);
|
|
698
475
|
}
|
|
699
476
|
const libDir = path2.join(baseDir, "lib", "software");
|
|
700
477
|
fs2.mkdirSync(libDir, { recursive: true });
|
|
701
478
|
if (wantsClient) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
console.log(pc2.green(" Created"), relativePath(cwd, clientPath));
|
|
708
|
-
}
|
|
479
|
+
writeFileIfAbsent(
|
|
480
|
+
cwd,
|
|
481
|
+
path2.join(libDir, "client.ts"),
|
|
482
|
+
getClientTemplate(env, publishableKeyEnvVar)
|
|
483
|
+
);
|
|
709
484
|
}
|
|
710
485
|
if (wantsReactQuery) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
console.log(pc2.green(" Created"), relativePath(cwd, queryProviderPath));
|
|
717
|
-
}
|
|
486
|
+
writeFileIfAbsent(
|
|
487
|
+
cwd,
|
|
488
|
+
path2.join(libDir, "query-provider.tsx"),
|
|
489
|
+
getQueryProviderTemplate(env)
|
|
490
|
+
);
|
|
718
491
|
}
|
|
719
492
|
if (wantsServer) {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
console.log(pc2.green(" Created"), relativePath(cwd, serverPath));
|
|
726
|
-
}
|
|
493
|
+
writeFileIfAbsent(
|
|
494
|
+
cwd,
|
|
495
|
+
path2.join(libDir, "server.ts"),
|
|
496
|
+
getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR)
|
|
497
|
+
);
|
|
727
498
|
}
|
|
728
499
|
if (env !== "vanilla" && env !== "edge" && answers.authMethod !== "browser") {
|
|
729
|
-
|
|
730
|
-
|
|
500
|
+
writeEnv(
|
|
501
|
+
cwd,
|
|
731
502
|
answers.publishableKey || "",
|
|
732
503
|
answers.secretKey || "",
|
|
733
504
|
publishableKeyEnvVar,
|
|
734
505
|
wantsServer ? SECRET_KEY_ENV_VAR : null
|
|
735
506
|
);
|
|
736
|
-
if (fs2.existsSync(envPath)) {
|
|
737
|
-
const existing = fs2.readFileSync(envPath, "utf-8");
|
|
738
|
-
if (existing.includes(publishableKeyEnvVar)) {
|
|
739
|
-
console.log(pc2.yellow(" Skipped"), ".env", pc2.dim("(keys already present)"));
|
|
740
|
-
} else {
|
|
741
|
-
fs2.appendFileSync(envPath, envContent);
|
|
742
|
-
console.log(pc2.green(" Updated"), ".env");
|
|
743
|
-
}
|
|
744
|
-
} else {
|
|
745
|
-
fs2.writeFileSync(envPath, envContent.trimStart());
|
|
746
|
-
console.log(pc2.green(" Created"), ".env");
|
|
747
|
-
}
|
|
748
507
|
}
|
|
749
508
|
let publishableKey = answers.publishableKey;
|
|
750
509
|
let secretKey = answers.secretKey;
|
|
@@ -757,23 +516,14 @@ async function init(cwd, info, answers) {
|
|
|
757
516
|
secretKey = creds.secretKey;
|
|
758
517
|
tenantName = creds.tenantName;
|
|
759
518
|
if (env !== "vanilla" && env !== "edge" && publishableKey) {
|
|
760
|
-
|
|
761
|
-
|
|
519
|
+
writeEnv(
|
|
520
|
+
cwd,
|
|
762
521
|
publishableKey,
|
|
763
522
|
secretKey,
|
|
764
523
|
publishableKeyEnvVar,
|
|
765
|
-
wantsServer ? SECRET_KEY_ENV_VAR : null
|
|
524
|
+
wantsServer ? SECRET_KEY_ENV_VAR : null,
|
|
525
|
+
true
|
|
766
526
|
);
|
|
767
|
-
if (fs2.existsSync(envPath)) {
|
|
768
|
-
const existing = fs2.readFileSync(envPath, "utf-8");
|
|
769
|
-
if (!existing.includes(publishableKeyEnvVar)) {
|
|
770
|
-
fs2.appendFileSync(envPath, envContent);
|
|
771
|
-
console.log(pc2.green(" Updated"), ".env", pc2.dim("(added API keys)"));
|
|
772
|
-
}
|
|
773
|
-
} else {
|
|
774
|
-
fs2.writeFileSync(envPath, envContent.trimStart());
|
|
775
|
-
console.log(pc2.green(" Created"), ".env");
|
|
776
|
-
}
|
|
777
527
|
}
|
|
778
528
|
} catch (err) {
|
|
779
529
|
console.log(
|
|
@@ -792,63 +542,147 @@ async function init(cwd, info, answers) {
|
|
|
792
542
|
await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName);
|
|
793
543
|
}
|
|
794
544
|
}
|
|
545
|
+
return { installFailed, installCmd: addCmd };
|
|
795
546
|
}
|
|
796
|
-
function
|
|
797
|
-
|
|
798
|
-
|
|
547
|
+
function buildAddCmd(pm, hasPnpmWs, deps) {
|
|
548
|
+
const pkgs = deps.join(" ");
|
|
549
|
+
switch (pm) {
|
|
550
|
+
case "pnpm":
|
|
551
|
+
return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`;
|
|
552
|
+
case "yarn":
|
|
553
|
+
return `yarn add ${pkgs}`;
|
|
554
|
+
case "bun":
|
|
555
|
+
return `bun add ${pkgs}`;
|
|
556
|
+
default:
|
|
557
|
+
return `npm install ${pkgs}`;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function writeFileIfAbsent(cwd, filePath, content) {
|
|
561
|
+
if (fs2.existsSync(filePath)) {
|
|
562
|
+
console.log(pc2.yellow(" Skipped"), relativePath(cwd, filePath), pc2.dim("(already exists)"));
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
fs2.writeFileSync(filePath, content);
|
|
566
|
+
console.log(pc2.green(" Created"), relativePath(cwd, filePath));
|
|
567
|
+
}
|
|
568
|
+
function writeEnv(cwd, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar, afterBrowserAuth = false) {
|
|
569
|
+
const envPath = path2.join(cwd, ".env");
|
|
570
|
+
const envContent = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar);
|
|
571
|
+
if (fs2.existsSync(envPath)) {
|
|
572
|
+
const existing = fs2.readFileSync(envPath, "utf-8");
|
|
573
|
+
if (existing.includes(publishableKeyEnvVar)) {
|
|
574
|
+
if (!afterBrowserAuth) {
|
|
575
|
+
console.log(pc2.yellow(" Skipped"), ".env", pc2.dim("(keys already present)"));
|
|
576
|
+
}
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
fs2.appendFileSync(envPath, envContent);
|
|
580
|
+
console.log(
|
|
581
|
+
pc2.green(" Updated"),
|
|
582
|
+
".env",
|
|
583
|
+
afterBrowserAuth ? pc2.dim("(added API keys)") : ""
|
|
584
|
+
);
|
|
585
|
+
} else {
|
|
586
|
+
fs2.writeFileSync(envPath, envContent.trimStart());
|
|
587
|
+
console.log(pc2.green(" Created"), ".env");
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function resolveMcpLocation(tool, cwd) {
|
|
591
|
+
const home = os.homedir();
|
|
799
592
|
switch (tool) {
|
|
800
593
|
case "claude":
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
594
|
+
return {
|
|
595
|
+
kind: "json",
|
|
596
|
+
absolutePath: path2.join(cwd, ".mcp.json"),
|
|
597
|
+
displayPath: ".mcp.json",
|
|
598
|
+
gitignoreEntry: ".mcp.json"
|
|
599
|
+
};
|
|
804
600
|
case "cursor":
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
601
|
+
return {
|
|
602
|
+
kind: "json",
|
|
603
|
+
absolutePath: path2.join(cwd, ".cursor", "mcp.json"),
|
|
604
|
+
displayPath: ".cursor/mcp.json",
|
|
605
|
+
gitignoreEntry: ".cursor/mcp.json"
|
|
606
|
+
};
|
|
809
607
|
case "vscode":
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
608
|
+
return {
|
|
609
|
+
kind: "json",
|
|
610
|
+
absolutePath: path2.join(cwd, ".vscode", "mcp.json"),
|
|
611
|
+
displayPath: ".vscode/mcp.json",
|
|
612
|
+
gitignoreEntry: ".vscode/mcp.json"
|
|
613
|
+
};
|
|
814
614
|
case "windsurf": {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
configPath = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
821
|
-
displayPath = configPath;
|
|
822
|
-
fs2.mkdirSync(path2.dirname(configPath), { recursive: true });
|
|
823
|
-
break;
|
|
615
|
+
if (!home) return null;
|
|
616
|
+
const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
617
|
+
return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null };
|
|
824
618
|
}
|
|
825
|
-
|
|
826
|
-
return;
|
|
619
|
+
case "codex": {
|
|
620
|
+
if (!home) return null;
|
|
621
|
+
const p = path2.join(home, ".codex", "config.toml");
|
|
622
|
+
return { kind: "toml", absolutePath: p, displayPath: p, gitignoreEntry: null };
|
|
623
|
+
}
|
|
624
|
+
case "gemini": {
|
|
625
|
+
if (!home) return null;
|
|
626
|
+
const p = path2.join(home, ".gemini", "settings.json");
|
|
627
|
+
return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null };
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function writeMcpConfig(tool, cwd, apiKey) {
|
|
632
|
+
const loc = resolveMcpLocation(tool, cwd);
|
|
633
|
+
if (!loc) {
|
|
634
|
+
console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)"));
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true });
|
|
638
|
+
if (loc.kind === "json") {
|
|
639
|
+
writeJsonMcp(loc, apiKey);
|
|
640
|
+
} else {
|
|
641
|
+
writeTomlMcp(loc, apiKey);
|
|
827
642
|
}
|
|
828
|
-
|
|
643
|
+
}
|
|
644
|
+
function writeJsonMcp(loc, apiKey) {
|
|
645
|
+
if (fs2.existsSync(loc.absolutePath)) {
|
|
829
646
|
try {
|
|
830
|
-
const existing = JSON.parse(fs2.readFileSync(
|
|
647
|
+
const existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8"));
|
|
831
648
|
if (existing.mcpServers?.["01software"]) {
|
|
832
|
-
console.log(pc2.yellow(" Skipped"), displayPath, pc2.dim("(01software already configured)"));
|
|
649
|
+
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(01software already configured)"));
|
|
833
650
|
return;
|
|
834
651
|
}
|
|
835
652
|
existing.mcpServers = existing.mcpServers || {};
|
|
836
653
|
existing.mcpServers["01software"] = getMcpServerEntry(apiKey);
|
|
837
|
-
fs2.writeFileSync(
|
|
838
|
-
console.log(pc2.green(" Updated"), displayPath);
|
|
654
|
+
fs2.writeFileSync(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n");
|
|
655
|
+
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
839
656
|
} catch {
|
|
840
|
-
console.log(pc2.yellow(" Skipped"), displayPath, pc2.dim("(could not parse existing file)"));
|
|
657
|
+
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)"));
|
|
841
658
|
}
|
|
842
659
|
} else {
|
|
843
|
-
fs2.writeFileSync(
|
|
844
|
-
console.log(pc2.green(" Created"), displayPath);
|
|
660
|
+
fs2.writeFileSync(loc.absolutePath, getMcpConfigTemplate(apiKey));
|
|
661
|
+
console.log(pc2.green(" Created"), loc.displayPath);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
function writeTomlMcp(loc, apiKey) {
|
|
665
|
+
const section = getCodexMcpTomlSection(apiKey);
|
|
666
|
+
if (fs2.existsSync(loc.absolutePath)) {
|
|
667
|
+
const existing = fs2.readFileSync(loc.absolutePath, "utf-8");
|
|
668
|
+
if (existing.includes(CODEX_MCP_SECTION_MARKER)) {
|
|
669
|
+
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(01software already configured)"));
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const sep = existing.endsWith("\n") ? "" : "\n";
|
|
673
|
+
fs2.appendFileSync(loc.absolutePath, sep + section);
|
|
674
|
+
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
675
|
+
} else {
|
|
676
|
+
fs2.writeFileSync(loc.absolutePath, section.trimStart());
|
|
677
|
+
console.log(pc2.green(" Created"), loc.displayPath);
|
|
845
678
|
}
|
|
846
679
|
}
|
|
847
680
|
function addToGitignore(cwd, tools) {
|
|
848
681
|
const entries = [];
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
682
|
+
for (const tool of tools) {
|
|
683
|
+
const loc = resolveMcpLocation(tool, cwd);
|
|
684
|
+
if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry);
|
|
685
|
+
}
|
|
852
686
|
if (entries.length === 0) return;
|
|
853
687
|
const gitignorePath = path2.join(cwd, ".gitignore");
|
|
854
688
|
const existing = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf-8") : "";
|
|
@@ -966,7 +800,7 @@ var OTHER_FRAMEWORK_GUIDE = `
|
|
|
966
800
|
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
967
801
|
})
|
|
968
802
|
|
|
969
|
-
4. Docs: https://01.software/docs/
|
|
803
|
+
4. Docs: https://01.software/docs/developers/sdk/client
|
|
970
804
|
`;
|
|
971
805
|
async function main() {
|
|
972
806
|
const cwd = process.cwd();
|
|
@@ -975,7 +809,7 @@ async function main() {
|
|
|
975
809
|
console.log(pc3.dim(" Initialize 01.software SDK in your project"));
|
|
976
810
|
console.log();
|
|
977
811
|
const info = detectProject(cwd);
|
|
978
|
-
if (!info.hasPackageJson) {
|
|
812
|
+
if (!info.hasPackageJson || info.parseError) {
|
|
979
813
|
if (info.parseError) {
|
|
980
814
|
console.log(pc3.red(" Could not parse package.json (invalid JSON)."));
|
|
981
815
|
console.log(pc3.dim(" Fix the syntax error and try again."));
|
|
@@ -1007,7 +841,7 @@ async function main() {
|
|
|
1007
841
|
const resolvedPm = info.packageManager ?? answers.packageManager ?? "npm";
|
|
1008
842
|
const resolvedInfo = { ...info, packageManager: resolvedPm };
|
|
1009
843
|
console.log();
|
|
1010
|
-
await init(cwd, resolvedInfo, answers);
|
|
844
|
+
const result = await init(cwd, resolvedInfo, answers);
|
|
1011
845
|
const env = answers.env;
|
|
1012
846
|
const run = resolvedPm === "npm" ? "npm run" : resolvedPm;
|
|
1013
847
|
console.log();
|
|
@@ -1015,6 +849,11 @@ async function main() {
|
|
|
1015
849
|
console.log();
|
|
1016
850
|
console.log(" Next steps:");
|
|
1017
851
|
console.log();
|
|
852
|
+
if (result.installFailed) {
|
|
853
|
+
console.log(pc3.yellow(" Install the SDK manually:"));
|
|
854
|
+
console.log(pc3.cyan(` ${result.installCmd}`));
|
|
855
|
+
console.log();
|
|
856
|
+
}
|
|
1018
857
|
if (env === "nextjs") {
|
|
1019
858
|
console.log(pc3.dim(" Add QueryProvider to your root layout:"));
|
|
1020
859
|
console.log();
|
|
@@ -1053,7 +892,7 @@ async function main() {
|
|
|
1053
892
|
console.log();
|
|
1054
893
|
}
|
|
1055
894
|
if (answers.aiTools.length > 0 && (!answers.publishableKey || !answers.secretKey)) {
|
|
1056
|
-
console.log(pc3.dim(" Update
|
|
895
|
+
console.log(pc3.dim(" Update MCP config x-api-key with your sk01_... token"));
|
|
1057
896
|
console.log();
|
|
1058
897
|
}
|
|
1059
898
|
if (env !== "vanilla") {
|