@01.software/init 0.7.0 → 0.9.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 +1 -1
- package/dist/chunk-2IGKOSK7.js +821 -0
- package/dist/chunk-2IGKOSK7.js.map +1 -0
- package/dist/{chunk-T3A5SLEJ.js → chunk-5K2CB2Y5.js} +34 -20
- package/dist/chunk-5K2CB2Y5.js.map +1 -0
- package/dist/{chunk-S3KHPWCE.js → chunk-TBGKXE3Q.js} +8 -5
- package/dist/chunk-TBGKXE3Q.js.map +1 -0
- package/dist/chunk-UA7WNT2F.js +118 -0
- package/dist/chunk-UA7WNT2F.js.map +1 -0
- package/dist/file-ops.js +13 -3
- package/dist/index.js +57 -823
- package/dist/index.js.map +1 -1
- package/dist/init.js +11 -0
- package/dist/init.js.map +1 -0
- package/dist/templates.js +3 -1
- package/package.json +5 -4
- package/dist/chunk-JT3G6B66.js +0 -56
- package/dist/chunk-JT3G6B66.js.map +0 -1
- package/dist/chunk-S3KHPWCE.js.map +0 -1
- package/dist/chunk-T3A5SLEJ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,112 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
replaceTomlMcpSection,
|
|
10
|
-
setEnvValue
|
|
11
|
-
} from "./chunk-JT3G6B66.js";
|
|
12
|
-
import {
|
|
13
|
-
CODEX_MCP_SECTION_MARKER,
|
|
14
|
-
getAnalyticsTemplate,
|
|
15
|
-
getClientTemplate,
|
|
16
|
-
getCodexMcpTomlSection,
|
|
17
|
-
getEnvContent,
|
|
18
|
-
getMcpConfigTemplate,
|
|
19
|
-
getMcpServerEntry,
|
|
20
|
-
getQueryProviderTemplate,
|
|
21
|
-
getServerTemplate
|
|
22
|
-
} from "./chunk-S3KHPWCE.js";
|
|
3
|
+
detectProject,
|
|
4
|
+
init
|
|
5
|
+
} from "./chunk-2IGKOSK7.js";
|
|
6
|
+
import "./chunk-5K2CB2Y5.js";
|
|
7
|
+
import "./chunk-UA7WNT2F.js";
|
|
8
|
+
import "./chunk-TBGKXE3Q.js";
|
|
23
9
|
|
|
24
10
|
// src/index.ts
|
|
25
|
-
import
|
|
26
|
-
|
|
27
|
-
// src/detect.ts
|
|
28
|
-
import fs from "fs";
|
|
29
|
-
import path from "path";
|
|
30
|
-
function detectProject(cwd) {
|
|
31
|
-
const pkgPath = path.join(cwd, "package.json");
|
|
32
|
-
const hasPackageJson = fs.existsSync(pkgPath);
|
|
33
|
-
let env = "node";
|
|
34
|
-
let hasSdk = false;
|
|
35
|
-
let hasReactQuery = false;
|
|
36
|
-
let parseError = false;
|
|
37
|
-
if (hasPackageJson) {
|
|
38
|
-
let pkg;
|
|
39
|
-
try {
|
|
40
|
-
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
41
|
-
} catch {
|
|
42
|
-
return {
|
|
43
|
-
hasPackageJson: true,
|
|
44
|
-
parseError: true,
|
|
45
|
-
env: "node",
|
|
46
|
-
packageManager: null,
|
|
47
|
-
hasSdk: false,
|
|
48
|
-
hasReactQuery: false,
|
|
49
|
-
srcDir: false
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
const deps = {
|
|
53
|
-
...pkg.dependencies || {},
|
|
54
|
-
...pkg.devDependencies || {}
|
|
55
|
-
};
|
|
56
|
-
hasSdk = "@01.software/sdk" in deps;
|
|
57
|
-
hasReactQuery = "@tanstack/react-query" in deps;
|
|
58
|
-
if ("next" in deps) {
|
|
59
|
-
env = "nextjs";
|
|
60
|
-
} else if ("astro" in deps || "@astrojs/node" in deps) {
|
|
61
|
-
env = "other";
|
|
62
|
-
} else if ("@remix-run/node" in deps || "@remix-run/react" in deps) {
|
|
63
|
-
env = "other";
|
|
64
|
-
} else if ("@sveltejs/kit" in deps) {
|
|
65
|
-
env = "other";
|
|
66
|
-
} else if ("react" in deps) {
|
|
67
|
-
if ("vite" in deps) {
|
|
68
|
-
env = "react-vite";
|
|
69
|
-
} else if ("react-scripts" in deps) {
|
|
70
|
-
env = "react-cra";
|
|
71
|
-
} else {
|
|
72
|
-
env = "node";
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
let packageManager = null;
|
|
77
|
-
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
|
|
78
|
-
packageManager = "pnpm";
|
|
79
|
-
} else if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
|
|
80
|
-
packageManager = "yarn";
|
|
81
|
-
} else if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) {
|
|
82
|
-
packageManager = "bun";
|
|
83
|
-
} else if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
|
|
84
|
-
packageManager = "npm";
|
|
85
|
-
}
|
|
86
|
-
const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src"));
|
|
87
|
-
return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir };
|
|
88
|
-
}
|
|
89
|
-
function needsClient(env) {
|
|
90
|
-
return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla";
|
|
91
|
-
}
|
|
92
|
-
function needsServer(env) {
|
|
93
|
-
return env === "nextjs" || env === "node" || env === "edge";
|
|
94
|
-
}
|
|
95
|
-
function needsReactQuery(env) {
|
|
96
|
-
return env === "nextjs" || env === "react-vite" || env === "react-cra";
|
|
97
|
-
}
|
|
98
|
-
function getPublishableKeyEnvVar(env) {
|
|
99
|
-
switch (env) {
|
|
100
|
-
case "nextjs":
|
|
101
|
-
return "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY";
|
|
102
|
-
case "react-vite":
|
|
103
|
-
return "VITE_SOFTWARE_PUBLISHABLE_KEY";
|
|
104
|
-
case "react-cra":
|
|
105
|
-
return "REACT_APP_SOFTWARE_PUBLISHABLE_KEY";
|
|
106
|
-
default:
|
|
107
|
-
return "SOFTWARE_PUBLISHABLE_KEY";
|
|
108
|
-
}
|
|
109
|
-
}
|
|
11
|
+
import pc from "picocolors";
|
|
110
12
|
|
|
111
13
|
// src/prompts.ts
|
|
112
14
|
import prompts from "prompts";
|
|
@@ -269,682 +171,6 @@ async function promptUser(hasSdk, detectedEnv, detectedPm) {
|
|
|
269
171
|
};
|
|
270
172
|
}
|
|
271
173
|
|
|
272
|
-
// src/init.ts
|
|
273
|
-
import fs2 from "fs";
|
|
274
|
-
import path2 from "path";
|
|
275
|
-
import os from "os";
|
|
276
|
-
import { execSync } from "child_process";
|
|
277
|
-
import pc2 from "picocolors";
|
|
278
|
-
import prompts2 from "prompts";
|
|
279
|
-
|
|
280
|
-
// src/browser-auth.ts
|
|
281
|
-
import { randomBytes } from "crypto";
|
|
282
|
-
import { createServer } from "http";
|
|
283
|
-
import { execFile, exec } from "child_process";
|
|
284
|
-
import { platform } from "os";
|
|
285
|
-
import { URL } from "url";
|
|
286
|
-
import pc from "picocolors";
|
|
287
|
-
var DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || "https://01.software";
|
|
288
|
-
var TIMEOUT_MS = 5 * 60 * 1e3;
|
|
289
|
-
function escapeHtml(s) {
|
|
290
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
291
|
-
}
|
|
292
|
-
function openBrowser(url) {
|
|
293
|
-
const os2 = platform();
|
|
294
|
-
const onError = () => {
|
|
295
|
-
console.log(
|
|
296
|
-
pc.yellow(
|
|
297
|
-
`Could not open browser automatically. Open this URL manually:
|
|
298
|
-
${url}`
|
|
299
|
-
)
|
|
300
|
-
);
|
|
301
|
-
};
|
|
302
|
-
if (os2 === "win32") {
|
|
303
|
-
exec(`start "" "${url}"`, (err) => {
|
|
304
|
-
if (err) onError();
|
|
305
|
-
});
|
|
306
|
-
} else {
|
|
307
|
-
const cmd = os2 === "darwin" ? "open" : "xdg-open";
|
|
308
|
-
execFile(cmd, [url], (err) => {
|
|
309
|
-
if (err) onError();
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
var PAGE_STYLE = `*{margin:0;box-sizing:border-box}
|
|
314
|
-
body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}
|
|
315
|
-
@media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}
|
|
316
|
-
.card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}
|
|
317
|
-
.icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}
|
|
318
|
-
.icon.ok{background:rgba(0,0,0,.05);color:#252525}
|
|
319
|
-
.icon.err{background:rgba(220,38,38,.08);color:#dc2626}
|
|
320
|
-
@media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}
|
|
321
|
-
h1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}
|
|
322
|
-
p{font-size:.75rem;color:#737373;line-height:1.5}`;
|
|
323
|
-
var SUCCESS_HTML = `<!DOCTYPE html>
|
|
324
|
-
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login</title>
|
|
325
|
-
<style>${PAGE_STYLE}</style>
|
|
326
|
-
</head><body><div class="card"><div class="icon ok">\u2713</div><h1>Authenticated</h1><p>You can close this tab and return to the terminal.</p></div></body></html>`;
|
|
327
|
-
var ERROR_HTML = (msg) => `<!DOCTYPE html>
|
|
328
|
-
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login Error</title>
|
|
329
|
-
<style>${PAGE_STYLE}</style>
|
|
330
|
-
</head><body><div class="card"><div class="icon err">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`;
|
|
331
|
-
async function exchangeCode(webUrl, code) {
|
|
332
|
-
const url = `${webUrl}/api/cli/exchange`;
|
|
333
|
-
try {
|
|
334
|
-
const res = await fetch(url, {
|
|
335
|
-
method: "POST",
|
|
336
|
-
headers: { "Content-Type": "application/json" },
|
|
337
|
-
body: JSON.stringify({ code })
|
|
338
|
-
});
|
|
339
|
-
if (!res.ok) {
|
|
340
|
-
const body = await res.text().catch(() => "");
|
|
341
|
-
console.error(
|
|
342
|
-
pc.red(
|
|
343
|
-
`Exchange failed: HTTP ${res.status} from ${url}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`
|
|
344
|
-
)
|
|
345
|
-
);
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
const data = await res.json();
|
|
349
|
-
if (typeof data.publishableKey !== "string" || typeof data.secretKey !== "string" || typeof data.tenantName !== "string" || typeof data.tenantId !== "string") {
|
|
350
|
-
console.error(pc.red(`Exchange failed: malformed response from ${url}`));
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
return {
|
|
354
|
-
publishableKey: data.publishableKey,
|
|
355
|
-
secretKey: data.secretKey,
|
|
356
|
-
tenantName: data.tenantName,
|
|
357
|
-
tenantId: data.tenantId
|
|
358
|
-
};
|
|
359
|
-
} catch (err) {
|
|
360
|
-
console.error(
|
|
361
|
-
pc.red(
|
|
362
|
-
`Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
363
|
-
)
|
|
364
|
-
);
|
|
365
|
-
return null;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
async function startBrowserAuth(options) {
|
|
369
|
-
const state = randomBytes(32).toString("hex");
|
|
370
|
-
const webUrl = options?.webUrl ?? DEFAULT_WEB_URL;
|
|
371
|
-
return new Promise((resolve, reject) => {
|
|
372
|
-
const server = createServer((req, res) => {
|
|
373
|
-
if (!req.url) {
|
|
374
|
-
res.writeHead(400).end();
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
const url = new URL(req.url, `http://localhost`);
|
|
378
|
-
if (url.pathname !== "/callback" || req.method !== "GET") {
|
|
379
|
-
res.writeHead(404).end();
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
const error = url.searchParams.get("error");
|
|
383
|
-
if (error) {
|
|
384
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML(error));
|
|
385
|
-
console.error(pc.red(`Login failed: ${error}`));
|
|
386
|
-
cleanup(new Error(`Login failed: ${error}`));
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
const code = url.searchParams.get("code");
|
|
390
|
-
const receivedState = url.searchParams.get("state");
|
|
391
|
-
if (!code || !receivedState) {
|
|
392
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Missing code or state."));
|
|
393
|
-
cleanup(new Error("Login failed: missing code or state."));
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
if (receivedState !== state) {
|
|
397
|
-
res.writeHead(403, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("State mismatch."));
|
|
398
|
-
console.error(pc.red("Login failed: state mismatch."));
|
|
399
|
-
cleanup(new Error("Login failed: state mismatch."));
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
exchangeCode(webUrl, code).then((creds) => {
|
|
403
|
-
if (!creds) {
|
|
404
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Invalid or expired code."));
|
|
405
|
-
cleanup(new Error("Login failed: code exchange failed."));
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(SUCCESS_HTML);
|
|
409
|
-
console.log(pc.green(`
|
|
410
|
-
Logged in successfully!`));
|
|
411
|
-
console.log(pc.dim(`Tenant: ${creds.tenantName}`));
|
|
412
|
-
cleanup(null, creds);
|
|
413
|
-
});
|
|
414
|
-
});
|
|
415
|
-
let timeout;
|
|
416
|
-
let completed = false;
|
|
417
|
-
function cleanup(err, result) {
|
|
418
|
-
if (completed) return;
|
|
419
|
-
completed = true;
|
|
420
|
-
clearTimeout(timeout);
|
|
421
|
-
server.closeAllConnections?.();
|
|
422
|
-
server.close(() => {
|
|
423
|
-
if (err) {
|
|
424
|
-
reject(err);
|
|
425
|
-
} else {
|
|
426
|
-
resolve(result);
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
server.listen(0, "127.0.0.1", () => {
|
|
431
|
-
const addr = server.address();
|
|
432
|
-
if (!addr || typeof addr === "string") {
|
|
433
|
-
reject(new Error("Failed to start local server."));
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
const port = addr.port;
|
|
437
|
-
timeout = setTimeout(() => {
|
|
438
|
-
console.error(pc.red("\nLogin timed out (5 minutes). Please try again."));
|
|
439
|
-
cleanup(new Error("Login timed out"));
|
|
440
|
-
}, TIMEOUT_MS);
|
|
441
|
-
const params = new URLSearchParams({ port: String(port), state });
|
|
442
|
-
if (options?.tenantId) {
|
|
443
|
-
params.set("tenantId", options.tenantId);
|
|
444
|
-
}
|
|
445
|
-
const loginUrl = `${webUrl}/cli-auth?${params.toString()}`;
|
|
446
|
-
console.log(pc.dim("Opening browser for login..."));
|
|
447
|
-
console.log(pc.dim(`If the browser does not open, visit:
|
|
448
|
-
${loginUrl}`));
|
|
449
|
-
openBrowser(loginUrl);
|
|
450
|
-
});
|
|
451
|
-
server.on("error", (err) => {
|
|
452
|
-
reject(err);
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// src/init.ts
|
|
458
|
-
var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY";
|
|
459
|
-
async function init(cwd, info, answers) {
|
|
460
|
-
const { packageManager, srcDir } = info;
|
|
461
|
-
const env = answers.env;
|
|
462
|
-
const baseDir = srcDir ? path2.join(cwd, "src") : cwd;
|
|
463
|
-
const publishableKeyEnvVar = getPublishableKeyEnvVar(env);
|
|
464
|
-
const wantsClient = needsClient(env);
|
|
465
|
-
const wantsServer = needsServer(env);
|
|
466
|
-
const wantsReactQuery = needsReactQuery(env);
|
|
467
|
-
const plan = await planConflictsAndEnv(cwd, baseDir, env, answers);
|
|
468
|
-
const installResult = installDeps(
|
|
469
|
-
cwd,
|
|
470
|
-
packageManager,
|
|
471
|
-
info.hasSdk,
|
|
472
|
-
info.hasReactQuery,
|
|
473
|
-
wantsReactQuery
|
|
474
|
-
);
|
|
475
|
-
if (wantsClient || wantsReactQuery || wantsServer) {
|
|
476
|
-
fs2.mkdirSync(path2.join(baseDir, "lib", "software"), { recursive: true });
|
|
477
|
-
}
|
|
478
|
-
const libDir = path2.join(baseDir, "lib", "software");
|
|
479
|
-
if (wantsClient) {
|
|
480
|
-
await writeFileWithPolicy(
|
|
481
|
-
cwd,
|
|
482
|
-
path2.join(libDir, "client.ts"),
|
|
483
|
-
getClientTemplate(env, publishableKeyEnvVar),
|
|
484
|
-
plan.policy
|
|
485
|
-
);
|
|
486
|
-
await writeFileWithPolicy(
|
|
487
|
-
cwd,
|
|
488
|
-
path2.join(libDir, "analytics.ts"),
|
|
489
|
-
getAnalyticsTemplate(env, publishableKeyEnvVar),
|
|
490
|
-
plan.policy
|
|
491
|
-
);
|
|
492
|
-
}
|
|
493
|
-
if (wantsReactQuery) {
|
|
494
|
-
await writeFileWithPolicy(
|
|
495
|
-
cwd,
|
|
496
|
-
path2.join(libDir, "query-provider.tsx"),
|
|
497
|
-
getQueryProviderTemplate(env),
|
|
498
|
-
plan.policy
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
if (wantsServer) {
|
|
502
|
-
await writeFileWithPolicy(
|
|
503
|
-
cwd,
|
|
504
|
-
path2.join(libDir, "server.ts"),
|
|
505
|
-
getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),
|
|
506
|
-
plan.policy
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
if (plan.envFile && answers.authMethod !== "browser") {
|
|
510
|
-
await writeEnv(
|
|
511
|
-
cwd,
|
|
512
|
-
plan.envFile,
|
|
513
|
-
answers.publishableKey || "",
|
|
514
|
-
answers.secretKey || "",
|
|
515
|
-
publishableKeyEnvVar,
|
|
516
|
-
wantsServer ? SECRET_KEY_ENV_VAR : null,
|
|
517
|
-
plan.policy
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
let publishableKey = answers.publishableKey;
|
|
521
|
-
let secretKey = answers.secretKey;
|
|
522
|
-
let tenantName = "";
|
|
523
|
-
if (answers.authMethod === "browser" && answers.aiTools.length > 0) {
|
|
524
|
-
try {
|
|
525
|
-
console.log();
|
|
526
|
-
const creds = await startBrowserAuth();
|
|
527
|
-
publishableKey = creds.publishableKey;
|
|
528
|
-
secretKey = creds.secretKey;
|
|
529
|
-
tenantName = creds.tenantName;
|
|
530
|
-
if (plan.envFile && publishableKey) {
|
|
531
|
-
await writeEnv(
|
|
532
|
-
cwd,
|
|
533
|
-
plan.envFile,
|
|
534
|
-
publishableKey,
|
|
535
|
-
secretKey,
|
|
536
|
-
publishableKeyEnvVar,
|
|
537
|
-
wantsServer ? SECRET_KEY_ENV_VAR : null,
|
|
538
|
-
"overwrite",
|
|
539
|
-
true
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
} catch (err) {
|
|
543
|
-
console.log(
|
|
544
|
-
pc2.yellow(" Browser auth skipped:"),
|
|
545
|
-
err instanceof Error ? err.message : String(err)
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if (answers.aiTools.length > 0) {
|
|
550
|
-
for (const tool of answers.aiTools) {
|
|
551
|
-
await writeMcpConfig(tool, cwd);
|
|
552
|
-
}
|
|
553
|
-
addToGitignore(cwd, answers.aiTools);
|
|
554
|
-
if (answers.aiTools.includes("claude")) {
|
|
555
|
-
await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
return installResult;
|
|
559
|
-
}
|
|
560
|
-
function installDeps(cwd, pm, hasSdk, hasReactQuery, wantsReactQuery) {
|
|
561
|
-
const allDeps = [
|
|
562
|
-
{ name: "@01.software/sdk", installed: hasSdk, needed: true },
|
|
563
|
-
{ name: "@tanstack/react-query", installed: hasReactQuery, needed: wantsReactQuery }
|
|
564
|
-
];
|
|
565
|
-
const fullList = allDeps.filter((d) => d.needed).map((d) => d.name);
|
|
566
|
-
const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name);
|
|
567
|
-
const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList);
|
|
568
|
-
if (toInstall.length === 0) {
|
|
569
|
-
console.log(pc2.dim(` Dependencies already installed: ${fullList.join(", ")}`));
|
|
570
|
-
return { installFailed: false, installSkipped: true, installCmd: fullCmd };
|
|
571
|
-
}
|
|
572
|
-
const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall);
|
|
573
|
-
console.log(pc2.dim(` Installing ${toInstall.join(" and ")}...`));
|
|
574
|
-
const wsPatched = pm === "pnpm" && patchPnpmWorkspace(cwd);
|
|
575
|
-
let installFailed = false;
|
|
576
|
-
try {
|
|
577
|
-
execSync(addCmd, { cwd, stdio: "pipe" });
|
|
578
|
-
console.log(pc2.green(" Installed"), toInstall.join(", "));
|
|
579
|
-
} catch (error) {
|
|
580
|
-
installFailed = true;
|
|
581
|
-
const err = error;
|
|
582
|
-
const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error);
|
|
583
|
-
console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding"));
|
|
584
|
-
const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n");
|
|
585
|
-
if (firstLines) console.log(pc2.dim(firstLines));
|
|
586
|
-
console.log(pc2.dim(` Run manually: ${addCmd}`));
|
|
587
|
-
} finally {
|
|
588
|
-
if (wsPatched) restorePnpmWorkspace(cwd);
|
|
589
|
-
}
|
|
590
|
-
return { installFailed, installSkipped: false, installCmd: addCmd };
|
|
591
|
-
}
|
|
592
|
-
function buildAddCmd(pm, hasPnpmWs, deps) {
|
|
593
|
-
const pkgs = deps.join(" ");
|
|
594
|
-
switch (pm) {
|
|
595
|
-
case "pnpm":
|
|
596
|
-
return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`;
|
|
597
|
-
case "yarn":
|
|
598
|
-
return `yarn add ${pkgs}`;
|
|
599
|
-
case "bun":
|
|
600
|
-
return `bun add ${pkgs}`;
|
|
601
|
-
default:
|
|
602
|
-
return `npm install ${pkgs}`;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
async function planConflictsAndEnv(cwd, baseDir, env, answers) {
|
|
606
|
-
const candidates = [];
|
|
607
|
-
const libDir = path2.join(baseDir, "lib", "software");
|
|
608
|
-
if (needsClient(env)) candidates.push(path2.join(libDir, "client.ts"));
|
|
609
|
-
if (needsReactQuery(env)) candidates.push(path2.join(libDir, "query-provider.tsx"));
|
|
610
|
-
if (needsServer(env)) candidates.push(path2.join(libDir, "server.ts"));
|
|
611
|
-
if (answers.aiTools.includes("claude")) {
|
|
612
|
-
for (const { dirName } of getSkillFiles()) {
|
|
613
|
-
candidates.push(path2.join(cwd, ".claude", "skills", dirName, "SKILL.md"));
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
const conflicts = candidates.filter((p) => fs2.existsSync(p));
|
|
617
|
-
let policy = "skip";
|
|
618
|
-
if (conflicts.length > 0) {
|
|
619
|
-
console.log(pc2.yellow(` ${conflicts.length} file(s) already exist:`));
|
|
620
|
-
for (const c of conflicts) console.log(pc2.dim(` ${path2.relative(cwd, c)}`));
|
|
621
|
-
const { selected } = await prompts2({
|
|
622
|
-
type: "select",
|
|
623
|
-
name: "selected",
|
|
624
|
-
message: "How should I handle existing files?",
|
|
625
|
-
choices: [
|
|
626
|
-
{ title: "Keep existing (skip)", value: "skip" },
|
|
627
|
-
{ title: "Overwrite all", value: "overwrite" },
|
|
628
|
-
{ title: "Ask for each", value: "ask" }
|
|
629
|
-
],
|
|
630
|
-
initial: 0
|
|
631
|
-
});
|
|
632
|
-
policy = selected ?? "skip";
|
|
633
|
-
}
|
|
634
|
-
const envFile = env === "vanilla" || env === "edge" ? "" : await pickEnvFile(cwd, env);
|
|
635
|
-
return { policy, envFile };
|
|
636
|
-
}
|
|
637
|
-
async function pickEnvFile(cwd, env) {
|
|
638
|
-
const candidates = [".env.local", ".env", ".env.development"];
|
|
639
|
-
const existing = candidates.filter((f) => fs2.existsSync(path2.join(cwd, f)));
|
|
640
|
-
const preferred = env === "nextjs" ? ".env.local" : ".env";
|
|
641
|
-
if (existing.length === 0) return preferred;
|
|
642
|
-
if (existing.length === 1 && existing[0] === preferred) return existing[0];
|
|
643
|
-
const options = Array.from(/* @__PURE__ */ new Set([...existing, preferred]));
|
|
644
|
-
const choices = options.map((f) => ({
|
|
645
|
-
title: f,
|
|
646
|
-
description: existing.includes(f) ? "exists" : "create",
|
|
647
|
-
value: f
|
|
648
|
-
}));
|
|
649
|
-
const initial = Math.max(
|
|
650
|
-
0,
|
|
651
|
-
choices.findIndex((c) => c.value === preferred)
|
|
652
|
-
);
|
|
653
|
-
const { file } = await prompts2({
|
|
654
|
-
type: "select",
|
|
655
|
-
name: "file",
|
|
656
|
-
message: "Which env file should I write SDK credentials to?",
|
|
657
|
-
choices,
|
|
658
|
-
initial
|
|
659
|
-
});
|
|
660
|
-
return file ?? preferred;
|
|
661
|
-
}
|
|
662
|
-
async function writeFileWithPolicy(cwd, filePath, content, policy) {
|
|
663
|
-
const rel = path2.relative(cwd, filePath);
|
|
664
|
-
if (!fs2.existsSync(filePath)) {
|
|
665
|
-
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
666
|
-
fs2.writeFileSync(filePath, content);
|
|
667
|
-
console.log(pc2.green(" Created"), rel);
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
const existing = fs2.readFileSync(filePath, "utf-8");
|
|
671
|
-
if (existing === content) {
|
|
672
|
-
console.log(pc2.dim(" Unchanged"), rel);
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
let shouldWrite = false;
|
|
676
|
-
if (policy === "overwrite") {
|
|
677
|
-
shouldWrite = true;
|
|
678
|
-
} else if (policy === "ask") {
|
|
679
|
-
const { confirm } = await prompts2({
|
|
680
|
-
type: "confirm",
|
|
681
|
-
name: "confirm",
|
|
682
|
-
message: `Overwrite ${rel}?`,
|
|
683
|
-
initial: false
|
|
684
|
-
});
|
|
685
|
-
shouldWrite = !!confirm;
|
|
686
|
-
}
|
|
687
|
-
if (shouldWrite) {
|
|
688
|
-
fs2.writeFileSync(filePath, content);
|
|
689
|
-
console.log(pc2.green(" Overwrote"), rel);
|
|
690
|
-
} else {
|
|
691
|
-
console.log(pc2.yellow(" Skipped"), rel, pc2.dim("(already exists)"));
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
async function writeEnv(cwd, envFile, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar, policy, fromBrowserAuth = false) {
|
|
695
|
-
const envPath = path2.join(cwd, envFile);
|
|
696
|
-
const targets = [
|
|
697
|
-
{ name: publishableKeyEnvVar, value: publishableKey }
|
|
698
|
-
];
|
|
699
|
-
if (secretKeyEnvVar) {
|
|
700
|
-
targets.push({ name: secretKeyEnvVar, value: secretKey });
|
|
701
|
-
}
|
|
702
|
-
if (!fs2.existsSync(envPath)) {
|
|
703
|
-
const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar);
|
|
704
|
-
fs2.writeFileSync(envPath, initial.trimStart());
|
|
705
|
-
console.log(pc2.green(" Created"), envFile);
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
let content = fs2.readFileSync(envPath, "utf-8");
|
|
709
|
-
let modified = false;
|
|
710
|
-
let appendedHeader = false;
|
|
711
|
-
const headerAlreadyPresent = targets.some(
|
|
712
|
-
(t) => readEnvValue(content, t.name) !== null
|
|
713
|
-
);
|
|
714
|
-
for (const { name, value } of targets) {
|
|
715
|
-
const existing = readEnvValue(content, name);
|
|
716
|
-
if (existing === null) {
|
|
717
|
-
if (!headerAlreadyPresent && !appendedHeader) {
|
|
718
|
-
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
719
|
-
content += "\n# 01.software\n";
|
|
720
|
-
appendedHeader = true;
|
|
721
|
-
}
|
|
722
|
-
content = setEnvValue(content, name, value);
|
|
723
|
-
modified = true;
|
|
724
|
-
continue;
|
|
725
|
-
}
|
|
726
|
-
if (existing === value) continue;
|
|
727
|
-
if (!value) continue;
|
|
728
|
-
let shouldOverwrite = false;
|
|
729
|
-
if (policy === "overwrite") {
|
|
730
|
-
shouldOverwrite = true;
|
|
731
|
-
} else if (policy === "ask") {
|
|
732
|
-
const { confirm } = await prompts2({
|
|
733
|
-
type: "confirm",
|
|
734
|
-
name: "confirm",
|
|
735
|
-
message: `${name} already set in ${envFile}. Overwrite?`,
|
|
736
|
-
initial: fromBrowserAuth
|
|
737
|
-
});
|
|
738
|
-
shouldOverwrite = !!confirm;
|
|
739
|
-
}
|
|
740
|
-
if (shouldOverwrite) {
|
|
741
|
-
content = setEnvValue(content, name, value);
|
|
742
|
-
modified = true;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
if (modified) {
|
|
746
|
-
fs2.writeFileSync(envPath, content);
|
|
747
|
-
console.log(
|
|
748
|
-
pc2.green(" Updated"),
|
|
749
|
-
envFile,
|
|
750
|
-
fromBrowserAuth ? pc2.dim("(SDK credentials)") : ""
|
|
751
|
-
);
|
|
752
|
-
} else {
|
|
753
|
-
console.log(pc2.dim(" Unchanged"), envFile);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
function resolveMcpLocation(tool, cwd) {
|
|
757
|
-
const home = os.homedir();
|
|
758
|
-
switch (tool) {
|
|
759
|
-
case "claude":
|
|
760
|
-
return {
|
|
761
|
-
kind: "json",
|
|
762
|
-
absolutePath: path2.join(cwd, ".mcp.json"),
|
|
763
|
-
displayPath: ".mcp.json",
|
|
764
|
-
gitignoreEntry: ".mcp.json"
|
|
765
|
-
};
|
|
766
|
-
case "cursor":
|
|
767
|
-
return {
|
|
768
|
-
kind: "json",
|
|
769
|
-
absolutePath: path2.join(cwd, ".cursor", "mcp.json"),
|
|
770
|
-
displayPath: ".cursor/mcp.json",
|
|
771
|
-
gitignoreEntry: ".cursor/mcp.json"
|
|
772
|
-
};
|
|
773
|
-
case "vscode":
|
|
774
|
-
return {
|
|
775
|
-
kind: "json",
|
|
776
|
-
absolutePath: path2.join(cwd, ".vscode", "mcp.json"),
|
|
777
|
-
displayPath: ".vscode/mcp.json",
|
|
778
|
-
gitignoreEntry: ".vscode/mcp.json"
|
|
779
|
-
};
|
|
780
|
-
case "windsurf": {
|
|
781
|
-
if (!home) return null;
|
|
782
|
-
const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
783
|
-
return {
|
|
784
|
-
kind: "json",
|
|
785
|
-
absolutePath: p,
|
|
786
|
-
jsonClient: "windsurf",
|
|
787
|
-
displayPath: p,
|
|
788
|
-
gitignoreEntry: null
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
case "codex": {
|
|
792
|
-
if (!home) return null;
|
|
793
|
-
const p = path2.join(home, ".codex", "config.toml");
|
|
794
|
-
return { kind: "toml", absolutePath: p, displayPath: p, gitignoreEntry: null };
|
|
795
|
-
}
|
|
796
|
-
case "gemini": {
|
|
797
|
-
if (!home) return null;
|
|
798
|
-
const p = path2.join(home, ".gemini", "settings.json");
|
|
799
|
-
return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null };
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
async function writeMcpConfig(tool, cwd) {
|
|
804
|
-
const loc = resolveMcpLocation(tool, cwd);
|
|
805
|
-
if (!loc) {
|
|
806
|
-
console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)"));
|
|
807
|
-
return;
|
|
808
|
-
}
|
|
809
|
-
fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true });
|
|
810
|
-
if (loc.kind === "json") {
|
|
811
|
-
writeJsonMcp(loc);
|
|
812
|
-
} else {
|
|
813
|
-
writeTomlMcp(loc);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
function writeJsonMcp(loc) {
|
|
817
|
-
if (!fs2.existsSync(loc.absolutePath)) {
|
|
818
|
-
fs2.writeFileSync(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient));
|
|
819
|
-
console.log(pc2.green(" Created"), loc.displayPath);
|
|
820
|
-
return;
|
|
821
|
-
}
|
|
822
|
-
let existing;
|
|
823
|
-
try {
|
|
824
|
-
existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8"));
|
|
825
|
-
} catch {
|
|
826
|
-
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)"));
|
|
827
|
-
return;
|
|
828
|
-
}
|
|
829
|
-
const nextEntry = getMcpServerEntry(loc.jsonClient);
|
|
830
|
-
if (existing.mcpServers?.["01software"] && JSON.stringify(existing.mcpServers["01software"]) === JSON.stringify(nextEntry)) {
|
|
831
|
-
console.log(pc2.dim(" Unchanged"), loc.displayPath);
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
existing.mcpServers = existing.mcpServers || {};
|
|
835
|
-
existing.mcpServers["01software"] = nextEntry;
|
|
836
|
-
fs2.writeFileSync(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n");
|
|
837
|
-
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
838
|
-
}
|
|
839
|
-
function writeTomlMcp(loc) {
|
|
840
|
-
const section = getCodexMcpTomlSection();
|
|
841
|
-
if (!fs2.existsSync(loc.absolutePath)) {
|
|
842
|
-
fs2.writeFileSync(loc.absolutePath, section.trimStart());
|
|
843
|
-
console.log(pc2.green(" Created"), loc.displayPath);
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
const existing = fs2.readFileSync(loc.absolutePath, "utf-8");
|
|
847
|
-
if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {
|
|
848
|
-
const sep = existing.endsWith("\n") ? "" : "\n";
|
|
849
|
-
fs2.appendFileSync(loc.absolutePath, sep + section);
|
|
850
|
-
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
|
-
const replaced = replaceTomlMcpSection(existing, section);
|
|
854
|
-
if (replaced === existing) {
|
|
855
|
-
console.log(pc2.dim(" Unchanged"), loc.displayPath);
|
|
856
|
-
return;
|
|
857
|
-
}
|
|
858
|
-
fs2.writeFileSync(loc.absolutePath, replaced);
|
|
859
|
-
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
860
|
-
}
|
|
861
|
-
function addToGitignore(cwd, tools) {
|
|
862
|
-
const entries = [];
|
|
863
|
-
for (const tool of tools) {
|
|
864
|
-
const loc = resolveMcpLocation(tool, cwd);
|
|
865
|
-
if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry);
|
|
866
|
-
}
|
|
867
|
-
if (entries.length === 0) return;
|
|
868
|
-
const gitignorePath = path2.join(cwd, ".gitignore");
|
|
869
|
-
const existing = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf-8") : "";
|
|
870
|
-
const toAdd = entries.filter((e) => !existing.includes(e));
|
|
871
|
-
if (toAdd.length === 0) return;
|
|
872
|
-
const content = "\n# MCP configs\n" + toAdd.join("\n") + "\n";
|
|
873
|
-
if (fs2.existsSync(gitignorePath)) {
|
|
874
|
-
fs2.appendFileSync(gitignorePath, content);
|
|
875
|
-
} else {
|
|
876
|
-
fs2.writeFileSync(gitignorePath, content.trimStart());
|
|
877
|
-
}
|
|
878
|
-
console.log(pc2.green(" Updated"), ".gitignore", pc2.dim(`(added ${toAdd.join(", ")})`));
|
|
879
|
-
}
|
|
880
|
-
async function writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, policy) {
|
|
881
|
-
let ctx = {
|
|
882
|
-
tenantName: tenantName || "Your Tenant",
|
|
883
|
-
features: void 0,
|
|
884
|
-
collections: void 0
|
|
885
|
-
};
|
|
886
|
-
if (publishableKey && secretKey) {
|
|
887
|
-
const fetched = await fetchTenantContext(publishableKey, secretKey);
|
|
888
|
-
if (fetched) {
|
|
889
|
-
ctx = {
|
|
890
|
-
tenantName: fetched.tenantName || ctx.tenantName,
|
|
891
|
-
features: fetched.features,
|
|
892
|
-
collections: fetched.collections
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
const claudeDir = path2.join(cwd, ".claude");
|
|
897
|
-
const softwareDir = path2.join(claudeDir, "01software");
|
|
898
|
-
const skillsDir = path2.join(claudeDir, "skills");
|
|
899
|
-
fs2.mkdirSync(softwareDir, { recursive: true });
|
|
900
|
-
fs2.mkdirSync(skillsDir, { recursive: true });
|
|
901
|
-
const contextPath = path2.join(softwareDir, "context.md");
|
|
902
|
-
const contextExists = fs2.existsSync(contextPath);
|
|
903
|
-
fs2.writeFileSync(contextPath, generateClaudeMd(ctx));
|
|
904
|
-
console.log(pc2.green(contextExists ? " Updated" : " Created"), ".claude/01software/context.md");
|
|
905
|
-
const claudeMdPath = path2.join(claudeDir, "CLAUDE.md");
|
|
906
|
-
const importLine = "@.claude/01software/context.md";
|
|
907
|
-
if (!fs2.existsSync(claudeMdPath)) {
|
|
908
|
-
fs2.writeFileSync(claudeMdPath, importLine + "\n");
|
|
909
|
-
console.log(pc2.green(" Created"), ".claude/CLAUDE.md");
|
|
910
|
-
} else {
|
|
911
|
-
const existing = fs2.readFileSync(claudeMdPath, "utf-8");
|
|
912
|
-
if (!existing.includes(importLine)) {
|
|
913
|
-
const prefix = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
914
|
-
fs2.appendFileSync(claudeMdPath, prefix + importLine + "\n");
|
|
915
|
-
console.log(pc2.green(" Updated"), ".claude/CLAUDE.md", pc2.dim("(added @import)"));
|
|
916
|
-
} else {
|
|
917
|
-
console.log(pc2.dim(" Unchanged"), ".claude/CLAUDE.md");
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
for (const { dirName, content } of getSkillFiles()) {
|
|
921
|
-
const skillDir = path2.join(skillsDir, dirName);
|
|
922
|
-
const skillPath = path2.join(skillDir, "SKILL.md");
|
|
923
|
-
fs2.mkdirSync(skillDir, { recursive: true });
|
|
924
|
-
await writeFileWithPolicy(cwd, skillPath, content, policy);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
var WS_FILE = "pnpm-workspace.yaml";
|
|
928
|
-
var WS_BACKUP = "pnpm-workspace.yaml.bak";
|
|
929
|
-
function hasPnpmWorkspace(cwd) {
|
|
930
|
-
return fs2.existsSync(path2.join(cwd, WS_FILE));
|
|
931
|
-
}
|
|
932
|
-
function patchPnpmWorkspace(cwd) {
|
|
933
|
-
const wsPath = path2.join(cwd, WS_FILE);
|
|
934
|
-
if (!fs2.existsSync(wsPath)) return false;
|
|
935
|
-
const content = fs2.readFileSync(wsPath, "utf-8");
|
|
936
|
-
if (content.includes("packages:")) return false;
|
|
937
|
-
fs2.copyFileSync(wsPath, path2.join(cwd, WS_BACKUP));
|
|
938
|
-
fs2.writeFileSync(wsPath, content.trimEnd() + "\npackages: []\n");
|
|
939
|
-
return true;
|
|
940
|
-
}
|
|
941
|
-
function restorePnpmWorkspace(cwd) {
|
|
942
|
-
const backupPath = path2.join(cwd, WS_BACKUP);
|
|
943
|
-
if (!fs2.existsSync(backupPath)) return;
|
|
944
|
-
fs2.copyFileSync(backupPath, path2.join(cwd, WS_FILE));
|
|
945
|
-
fs2.unlinkSync(backupPath);
|
|
946
|
-
}
|
|
947
|
-
|
|
948
174
|
// src/index.ts
|
|
949
175
|
var ENV_LABELS = {
|
|
950
176
|
nextjs: "Next.js",
|
|
@@ -973,22 +199,22 @@ var OTHER_FRAMEWORK_GUIDE = `
|
|
|
973
199
|
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
974
200
|
})
|
|
975
201
|
|
|
976
|
-
4. Docs: https://01.software/docs/developers/sdk/client
|
|
202
|
+
4. Docs: https://docs.01.software/docs/developers/sdk/client
|
|
977
203
|
`;
|
|
978
204
|
async function main() {
|
|
979
205
|
const cwd = process.cwd();
|
|
980
206
|
console.log();
|
|
981
|
-
console.log(
|
|
982
|
-
console.log(
|
|
207
|
+
console.log(pc.bold(" @01.software/init"));
|
|
208
|
+
console.log(pc.dim(" Initialize 01.software SDK in your project"));
|
|
983
209
|
console.log();
|
|
984
210
|
const info = detectProject(cwd);
|
|
985
211
|
if (!info.hasPackageJson || info.parseError) {
|
|
986
212
|
if (info.parseError) {
|
|
987
|
-
console.log(
|
|
988
|
-
console.log(
|
|
213
|
+
console.log(pc.red(" Could not parse package.json (invalid JSON)."));
|
|
214
|
+
console.log(pc.dim(" Fix the syntax error and try again."));
|
|
989
215
|
} else {
|
|
990
|
-
console.log(
|
|
991
|
-
console.log(
|
|
216
|
+
console.log(pc.red(" No package.json found in the current directory."));
|
|
217
|
+
console.log(pc.dim(" Run this command inside an existing project."));
|
|
992
218
|
}
|
|
993
219
|
console.log();
|
|
994
220
|
process.exit(1);
|
|
@@ -997,17 +223,17 @@ async function main() {
|
|
|
997
223
|
if (info.packageManager) detectedParts.push(info.packageManager);
|
|
998
224
|
if (info.srcDir) detectedParts.push("src/");
|
|
999
225
|
if (info.env !== "node") {
|
|
1000
|
-
console.log(
|
|
226
|
+
console.log(pc.dim(` Detected: ${detectedParts.join(" / ")}`));
|
|
1001
227
|
console.log();
|
|
1002
228
|
}
|
|
1003
229
|
try {
|
|
1004
230
|
const answers = await promptUser(info.hasSdk, info.env, info.packageManager);
|
|
1005
231
|
if (!answers) {
|
|
1006
|
-
console.log(
|
|
232
|
+
console.log(pc.yellow(" Cancelled."));
|
|
1007
233
|
process.exit(0);
|
|
1008
234
|
}
|
|
1009
235
|
if (answers.env === "other") {
|
|
1010
|
-
console.log(
|
|
236
|
+
console.log(pc.yellow(" Manual setup required for your framework:"));
|
|
1011
237
|
console.log(OTHER_FRAMEWORK_GUIDE);
|
|
1012
238
|
process.exit(0);
|
|
1013
239
|
}
|
|
@@ -1018,78 +244,86 @@ async function main() {
|
|
|
1018
244
|
const env = answers.env;
|
|
1019
245
|
const run = resolvedPm === "npm" ? "npm run" : resolvedPm;
|
|
1020
246
|
console.log();
|
|
1021
|
-
console.log(
|
|
247
|
+
console.log(pc.green(" Done!"));
|
|
1022
248
|
console.log();
|
|
1023
249
|
console.log(" Next steps:");
|
|
1024
250
|
console.log();
|
|
1025
251
|
if (result.installFailed) {
|
|
1026
|
-
console.log(
|
|
1027
|
-
console.log(
|
|
252
|
+
console.log(pc.yellow(" Install the SDK manually:"));
|
|
253
|
+
console.log(pc.cyan(` ${result.installCmd}`));
|
|
1028
254
|
console.log();
|
|
1029
255
|
}
|
|
1030
256
|
if (env === "nextjs") {
|
|
1031
|
-
console.log(
|
|
257
|
+
console.log(pc.dim(" Add QueryProvider to your root layout:"));
|
|
1032
258
|
console.log();
|
|
1033
|
-
console.log(
|
|
1034
|
-
console.log(
|
|
259
|
+
console.log(pc.cyan(" import { QueryProvider } from '@/lib/software/query-provider'"));
|
|
260
|
+
console.log(pc.cyan(" <QueryProvider>{children}</QueryProvider>"));
|
|
1035
261
|
console.log();
|
|
1036
|
-
console.log(
|
|
262
|
+
console.log(pc.dim(" Optional: start browser analytics with the generated helper:"));
|
|
1037
263
|
console.log();
|
|
1038
|
-
console.log(
|
|
1039
|
-
console.log(
|
|
264
|
+
console.log(pc.cyan(" import { analytics } from '@/lib/software/analytics'"));
|
|
265
|
+
console.log(pc.cyan(" analytics.track('signup')"));
|
|
1040
266
|
console.log();
|
|
1041
267
|
} else if (env === "react-vite" || env === "react-cra") {
|
|
1042
|
-
console.log(
|
|
268
|
+
console.log(pc.dim(" Wrap your app entry with QueryProvider:"));
|
|
1043
269
|
console.log();
|
|
1044
|
-
console.log(
|
|
1045
|
-
console.log(
|
|
270
|
+
console.log(pc.cyan(" import { QueryProvider } from './lib/software/query-provider'"));
|
|
271
|
+
console.log(pc.cyan(" <QueryProvider><App /></QueryProvider>"));
|
|
1046
272
|
console.log();
|
|
1047
|
-
console.log(
|
|
273
|
+
console.log(pc.dim(" Optional: start browser analytics with the generated helper:"));
|
|
1048
274
|
console.log();
|
|
1049
|
-
console.log(
|
|
1050
|
-
console.log(
|
|
275
|
+
console.log(pc.cyan(" import { analytics } from './lib/software/analytics'"));
|
|
276
|
+
console.log(pc.cyan(" analytics.track('signup')"));
|
|
1051
277
|
console.log();
|
|
1052
278
|
} else if (env === "vanilla") {
|
|
1053
|
-
console.log(
|
|
279
|
+
console.log(pc.dim(" Replace YOUR_PUBLISHABLE_KEY in lib/software/client.ts"));
|
|
1054
280
|
console.log();
|
|
1055
|
-
console.log(
|
|
1056
|
-
console.log(
|
|
281
|
+
console.log(pc.cyan(" import { client } from './lib/software/client'"));
|
|
282
|
+
console.log(pc.cyan(" const articles = await client.collections.from('articles').find()"));
|
|
1057
283
|
console.log();
|
|
1058
|
-
console.log(
|
|
284
|
+
console.log(pc.dim(" Optional: wire the analytics helper in lib/software/analytics.ts"));
|
|
1059
285
|
console.log();
|
|
1060
286
|
} else if (env === "node") {
|
|
1061
|
-
console.log(
|
|
287
|
+
console.log(pc.dim(" Use the server client:"));
|
|
1062
288
|
console.log();
|
|
1063
|
-
console.log(
|
|
1064
|
-
console.log(
|
|
289
|
+
console.log(pc.cyan(" import { serverClient } from './lib/software/server'"));
|
|
290
|
+
console.log(pc.cyan(" const articles = await serverClient.collections.from('articles').find()"));
|
|
1065
291
|
console.log();
|
|
1066
292
|
} else if (env === "edge") {
|
|
1067
|
-
console.log(
|
|
293
|
+
console.log(pc.dim(" Pass your env bindings to createEdgeClient():"));
|
|
1068
294
|
console.log();
|
|
1069
|
-
console.log(
|
|
1070
|
-
console.log(
|
|
295
|
+
console.log(pc.cyan(" import { createEdgeClient } from './lib/software/server'"));
|
|
296
|
+
console.log(pc.cyan(" const serverClient = createEdgeClient(env.PUBLISHABLE_KEY, env.SECRET_KEY)"));
|
|
1071
297
|
console.log();
|
|
1072
298
|
}
|
|
1073
|
-
const
|
|
1074
|
-
const
|
|
299
|
+
const effectivePublishableKey = result.publishableKey ?? answers.publishableKey;
|
|
300
|
+
const effectiveSecretKey = result.secretKey ?? answers.secretKey;
|
|
301
|
+
const missingPublishableKey = env !== "vanilla" && !effectivePublishableKey;
|
|
302
|
+
const missingSecretKey = (env === "nextjs" || env === "node") && !effectiveSecretKey;
|
|
1075
303
|
if (missingPublishableKey || missingSecretKey) {
|
|
1076
|
-
console.log(
|
|
304
|
+
console.log(pc.dim(" Update .env with your SDK credentials"));
|
|
1077
305
|
console.log();
|
|
1078
306
|
}
|
|
1079
307
|
if (answers.aiTools.length > 0) {
|
|
1080
|
-
console.log(
|
|
308
|
+
console.log(pc.dim(" MCP config uses OAuth discovery."));
|
|
309
|
+
console.log(pc.dim(" Agent discovery flow:"));
|
|
310
|
+
console.log(pc.cyan(" 1. Read https://01.software/llms.txt"));
|
|
311
|
+
console.log(pc.cyan(" 2. Connect https://mcp.01.software/mcp"));
|
|
312
|
+
console.log(pc.cyan(" 3. Read https://docs.01.software/skill.md"));
|
|
313
|
+
console.log(pc.cyan(" 4. Inspect https://docs.01.software/api/openapi"));
|
|
314
|
+
console.log(pc.cyan(" 5. Run local lint, typecheck, tests, and build"));
|
|
1081
315
|
console.log();
|
|
1082
316
|
}
|
|
1083
317
|
if (env !== "vanilla") {
|
|
1084
|
-
console.log(
|
|
318
|
+
console.log(pc.cyan(` ${run} dev`));
|
|
1085
319
|
console.log();
|
|
1086
320
|
}
|
|
1087
321
|
} catch (error) {
|
|
1088
322
|
if (error instanceof Error && error.message === "cancelled") {
|
|
1089
|
-
console.log(
|
|
323
|
+
console.log(pc.yellow(" Cancelled."));
|
|
1090
324
|
process.exit(0);
|
|
1091
325
|
}
|
|
1092
|
-
console.error(
|
|
326
|
+
console.error(pc.red(" Error:"), error);
|
|
1093
327
|
process.exit(1);
|
|
1094
328
|
}
|
|
1095
329
|
}
|