rush-ai 0.3.1 → 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/index.js
CHANGED
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
} from "./chunk-UJLVFMWM.js";
|
|
34
34
|
|
|
35
35
|
// src/index.ts
|
|
36
|
-
import
|
|
36
|
+
import chalk8 from "chalk";
|
|
37
37
|
import { Command } from "commander";
|
|
38
38
|
|
|
39
39
|
// src/output/formatters/csv.ts
|
|
@@ -286,7 +286,7 @@ function getApiBaseUrl() {
|
|
|
286
286
|
async function loginViaBrowser(jsonMode) {
|
|
287
287
|
const baseUrl = getApiBaseUrl();
|
|
288
288
|
const state = randomBytes(16).toString("hex");
|
|
289
|
-
return new Promise((
|
|
289
|
+
return new Promise((resolve9, reject) => {
|
|
290
290
|
const server = createServer(async (req, res) => {
|
|
291
291
|
try {
|
|
292
292
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
@@ -299,12 +299,13 @@ async function loginViaBrowser(jsonMode) {
|
|
|
299
299
|
const returnedState = url.searchParams.get("state");
|
|
300
300
|
const error = url.searchParams.get("error");
|
|
301
301
|
if (error) {
|
|
302
|
-
const desc = url.searchParams.get("error_description") ?? "
|
|
302
|
+
const desc = url.searchParams.get("error_description") ?? "\u8BA4\u8BC1\u5931\u8D25";
|
|
303
303
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
304
304
|
res.end(
|
|
305
305
|
getHtmlPage(
|
|
306
|
-
"
|
|
307
|
-
|
|
306
|
+
"\u8BA4\u8BC1\u5931\u8D25",
|
|
307
|
+
`\u9519\u8BEF\uFF1A${desc}\u3002\u4F60\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u3002`,
|
|
308
|
+
baseUrl
|
|
308
309
|
)
|
|
309
310
|
);
|
|
310
311
|
server.close();
|
|
@@ -315,8 +316,9 @@ async function loginViaBrowser(jsonMode) {
|
|
|
315
316
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
316
317
|
res.end(
|
|
317
318
|
getHtmlPage(
|
|
318
|
-
"
|
|
319
|
-
"
|
|
319
|
+
"\u8BA4\u8BC1\u5931\u8D25",
|
|
320
|
+
"\u56DE\u8C03\u53C2\u6570\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u3002\u4F60\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u3002",
|
|
321
|
+
baseUrl
|
|
320
322
|
)
|
|
321
323
|
);
|
|
322
324
|
server.close();
|
|
@@ -336,8 +338,9 @@ async function loginViaBrowser(jsonMode) {
|
|
|
336
338
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
337
339
|
res.end(
|
|
338
340
|
getHtmlPage(
|
|
339
|
-
"
|
|
340
|
-
"Token
|
|
341
|
+
"\u8BA4\u8BC1\u5931\u8D25",
|
|
342
|
+
"Token \u4EA4\u6362\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002\u4F60\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u3002",
|
|
343
|
+
baseUrl
|
|
341
344
|
)
|
|
342
345
|
);
|
|
343
346
|
server.close();
|
|
@@ -356,8 +359,10 @@ async function loginViaBrowser(jsonMode) {
|
|
|
356
359
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
357
360
|
res.end(
|
|
358
361
|
getHtmlPage(
|
|
359
|
-
"
|
|
360
|
-
"
|
|
362
|
+
"\u8BA4\u8BC1\u6210\u529F",
|
|
363
|
+
"\u4F60\u5DF2\u6210\u529F\u767B\u5F55\uFF0C\u53EF\u4EE5\u5173\u95ED\u6B64\u9875\u9762\u5E76\u8FD4\u56DE\u7EC8\u7AEF\u3002",
|
|
364
|
+
baseUrl,
|
|
365
|
+
expiresAt ?? void 0
|
|
361
366
|
)
|
|
362
367
|
);
|
|
363
368
|
server.close();
|
|
@@ -377,7 +382,7 @@ async function loginViaBrowser(jsonMode) {
|
|
|
377
382
|
);
|
|
378
383
|
}
|
|
379
384
|
}
|
|
380
|
-
|
|
385
|
+
resolve9();
|
|
381
386
|
} catch (err) {
|
|
382
387
|
server.close();
|
|
383
388
|
reject(
|
|
@@ -415,21 +420,37 @@ async function loginViaBrowser(jsonMode) {
|
|
|
415
420
|
function escapeHtml(text) {
|
|
416
421
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
417
422
|
}
|
|
418
|
-
function getHtmlPage(title, message) {
|
|
423
|
+
function getHtmlPage(title, message, serverUrl, expiresAt) {
|
|
424
|
+
const metaLines = [
|
|
425
|
+
`<span class="label">\u670D\u52A1\u5668</span> <span class="value">${escapeHtml(serverUrl)}</span>`
|
|
426
|
+
];
|
|
427
|
+
if (expiresAt) {
|
|
428
|
+
metaLines.push(
|
|
429
|
+
`<span class="label">\u6709\u6548\u671F\u81F3</span> <span class="value">${escapeHtml(new Date(expiresAt).toLocaleString("zh-CN"))}</span>`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
const metaHtml = metaLines.map((line) => `<div class="meta-row">${line}</div>`).join("\n ");
|
|
419
433
|
return `<!DOCTYPE html>
|
|
420
|
-
<html>
|
|
421
|
-
<head><title>Rush CLI - ${escapeHtml(title)}</title>
|
|
434
|
+
<html lang="zh-CN">
|
|
435
|
+
<head><meta charset="utf-8"><title>Rush CLI - ${escapeHtml(title)}</title>
|
|
422
436
|
<style>
|
|
423
|
-
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f5f5f5; }
|
|
424
|
-
.card { background: white; border-radius: 12px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); text-align: center; max-width:
|
|
425
|
-
h1 { color: #333; font-size: 1.5em; }
|
|
437
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f5f5f5; }
|
|
438
|
+
.card { background: white; border-radius: 12px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); text-align: center; max-width: 420px; }
|
|
439
|
+
h1 { color: #333; font-size: 1.5em; margin-bottom: 8px; }
|
|
426
440
|
p { color: #666; line-height: 1.6; }
|
|
441
|
+
.meta { margin-top: 16px; padding-top: 16px; border-top: 1px solid #eee; text-align: left; }
|
|
442
|
+
.meta-row { color: #888; font-size: 0.85em; line-height: 2; }
|
|
443
|
+
.meta-row .label { color: #999; }
|
|
444
|
+
.meta-row .value { color: #555; font-family: "SF Mono", Menlo, monospace; }
|
|
427
445
|
</style>
|
|
428
446
|
</head>
|
|
429
447
|
<body>
|
|
430
448
|
<div class="card">
|
|
431
449
|
<h1>${escapeHtml(title)}</h1>
|
|
432
450
|
<p>${escapeHtml(message)}</p>
|
|
451
|
+
<div class="meta">
|
|
452
|
+
${metaHtml}
|
|
453
|
+
</div>
|
|
433
454
|
</div>
|
|
434
455
|
</body>
|
|
435
456
|
</html>`;
|
|
@@ -571,11 +592,458 @@ function registerAuthCommand(program) {
|
|
|
571
592
|
});
|
|
572
593
|
}
|
|
573
594
|
|
|
595
|
+
// src/commands/check/index.ts
|
|
596
|
+
import { resolve } from "path";
|
|
597
|
+
import chalk2 from "chalk";
|
|
598
|
+
|
|
599
|
+
// src/commands/check/checks/database.ts
|
|
600
|
+
import { existsSync, readFileSync } from "fs";
|
|
601
|
+
import { join } from "path";
|
|
602
|
+
var PLACEHOLDER_PATTERNS = [
|
|
603
|
+
"your-",
|
|
604
|
+
"placeholder",
|
|
605
|
+
"xxx",
|
|
606
|
+
"example",
|
|
607
|
+
"<",
|
|
608
|
+
">",
|
|
609
|
+
"${",
|
|
610
|
+
"todo"
|
|
611
|
+
];
|
|
612
|
+
function isPlaceholder(value) {
|
|
613
|
+
const lower = value.toLowerCase();
|
|
614
|
+
return PLACEHOLDER_PATTERNS.some((p) => lower.includes(p));
|
|
615
|
+
}
|
|
616
|
+
function parseEnvFile(filePath) {
|
|
617
|
+
if (!existsSync(filePath)) return {};
|
|
618
|
+
try {
|
|
619
|
+
const content = readFileSync(filePath, "utf-8");
|
|
620
|
+
const vars = {};
|
|
621
|
+
for (const line of content.split("\n")) {
|
|
622
|
+
const trimmed = line.trim();
|
|
623
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
624
|
+
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
625
|
+
if (match) vars[match[1]] = match[2];
|
|
626
|
+
}
|
|
627
|
+
return vars;
|
|
628
|
+
} catch {
|
|
629
|
+
return {};
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function checkDatabase(projectPath, framework) {
|
|
633
|
+
const defaultInfo = {
|
|
634
|
+
detected: false,
|
|
635
|
+
type: null,
|
|
636
|
+
autoCreate: false
|
|
637
|
+
};
|
|
638
|
+
if (framework !== "nextjs") {
|
|
639
|
+
return {
|
|
640
|
+
checks: [],
|
|
641
|
+
info: defaultInfo
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
const checks = [];
|
|
645
|
+
const envVars = parseEnvFile(join(projectPath, ".env.local"));
|
|
646
|
+
const postgresUrl = envVars.USER_POSTGRESQL_URL;
|
|
647
|
+
if (postgresUrl && !isPlaceholder(postgresUrl) && (postgresUrl.startsWith("postgresql://") || postgresUrl.startsWith("postgres://"))) {
|
|
648
|
+
checks.push({
|
|
649
|
+
name: "database_config",
|
|
650
|
+
severity: "info",
|
|
651
|
+
message: "Valid PostgreSQL config found in .env.local \u2014 Rush will use your existing database"
|
|
652
|
+
});
|
|
653
|
+
return {
|
|
654
|
+
checks,
|
|
655
|
+
info: { detected: true, type: "postgresql", autoCreate: false }
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
const supabaseUrl = envVars.SUPABASE_URL;
|
|
659
|
+
const supabaseKey = envVars.SUPABASE_SERVICE_ROLE_KEY || envVars.SUPABASE_KEY;
|
|
660
|
+
if (supabaseUrl && supabaseKey && !isPlaceholder(supabaseUrl) && !isPlaceholder(supabaseKey) && supabaseUrl.startsWith("https://") && supabaseKey.length > 20) {
|
|
661
|
+
checks.push({
|
|
662
|
+
name: "database_config",
|
|
663
|
+
severity: "info",
|
|
664
|
+
message: "Valid Supabase config found in .env.local \u2014 Rush will use your existing database"
|
|
665
|
+
});
|
|
666
|
+
return {
|
|
667
|
+
checks,
|
|
668
|
+
info: { detected: true, type: "supabase", autoCreate: false }
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const hasPlaceholder = postgresUrl && isPlaceholder(postgresUrl) || supabaseUrl && isPlaceholder(supabaseUrl);
|
|
672
|
+
if (hasPlaceholder) {
|
|
673
|
+
checks.push({
|
|
674
|
+
name: "database_config",
|
|
675
|
+
severity: "warning",
|
|
676
|
+
message: "Database env vars in .env.local contain placeholder values \u2014 Rush will auto-create a Supabase database and overwrite them",
|
|
677
|
+
fix: "Either set real database credentials or remove the placeholder values"
|
|
678
|
+
});
|
|
679
|
+
} else {
|
|
680
|
+
checks.push({
|
|
681
|
+
name: "database_config",
|
|
682
|
+
severity: "info",
|
|
683
|
+
message: "No database config detected \u2014 Rush will auto-create a Supabase database for this Next.js project"
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
return {
|
|
687
|
+
checks,
|
|
688
|
+
info: { detected: false, type: null, autoCreate: true }
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/commands/check/checks/framework.ts
|
|
693
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
694
|
+
import { join as join2 } from "path";
|
|
695
|
+
var NEXT_CONFIG_FILES = [
|
|
696
|
+
"next.config.js",
|
|
697
|
+
"next.config.ts",
|
|
698
|
+
"next.config.mjs"
|
|
699
|
+
];
|
|
700
|
+
var VITE_CONFIG_FILES = ["vite.config.ts", "vite.config.js"];
|
|
701
|
+
function detectFramework(projectPath) {
|
|
702
|
+
const pkgPath = join2(projectPath, "package.json");
|
|
703
|
+
if (!existsSync2(pkgPath)) return "unknown";
|
|
704
|
+
try {
|
|
705
|
+
const raw = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
706
|
+
const deps = { ...raw.dependencies, ...raw.devDependencies };
|
|
707
|
+
if (deps.next) return "nextjs";
|
|
708
|
+
if (deps.vite || VITE_CONFIG_FILES.some((f) => existsSync2(join2(projectPath, f)))) {
|
|
709
|
+
return "vite";
|
|
710
|
+
}
|
|
711
|
+
return "unknown";
|
|
712
|
+
} catch {
|
|
713
|
+
return "unknown";
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
function checkFramework(projectPath) {
|
|
717
|
+
const checks = [];
|
|
718
|
+
const framework = detectFramework(projectPath);
|
|
719
|
+
checks.push({
|
|
720
|
+
name: "framework_detected",
|
|
721
|
+
severity: "info",
|
|
722
|
+
message: `Detected framework: ${framework}`
|
|
723
|
+
});
|
|
724
|
+
if (framework === "nextjs") {
|
|
725
|
+
const hasConfig = NEXT_CONFIG_FILES.some(
|
|
726
|
+
(f) => existsSync2(join2(projectPath, f))
|
|
727
|
+
);
|
|
728
|
+
if (!hasConfig) {
|
|
729
|
+
checks.push({
|
|
730
|
+
name: "config_file",
|
|
731
|
+
severity: "warning",
|
|
732
|
+
message: "No next.config.js/ts/mjs found",
|
|
733
|
+
fix: "Create a next.config.js file"
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
} else if (framework === "vite") {
|
|
737
|
+
const hasConfig = VITE_CONFIG_FILES.some(
|
|
738
|
+
(f) => existsSync2(join2(projectPath, f))
|
|
739
|
+
);
|
|
740
|
+
if (hasConfig) {
|
|
741
|
+
checks.push({
|
|
742
|
+
name: "config_file",
|
|
743
|
+
severity: "info",
|
|
744
|
+
message: "Vite config file found"
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
if (existsSync2(join2(projectPath, "index.html"))) {
|
|
748
|
+
checks.push({
|
|
749
|
+
name: "index_html",
|
|
750
|
+
severity: "info",
|
|
751
|
+
message: "index.html found (Vite entry point)"
|
|
752
|
+
});
|
|
753
|
+
} else {
|
|
754
|
+
checks.push({
|
|
755
|
+
name: "index_html",
|
|
756
|
+
severity: "warning",
|
|
757
|
+
message: "index.html not found \u2014 Vite projects typically need this as entry point",
|
|
758
|
+
fix: "Create an index.html at the project root"
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return checks;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/commands/check/checks/lock-file.ts
|
|
766
|
+
import { existsSync as existsSync3 } from "fs";
|
|
767
|
+
import { join as join3 } from "path";
|
|
768
|
+
function detectPackageManager(projectPath) {
|
|
769
|
+
if (existsSync3(join3(projectPath, "pnpm-lock.yaml"))) return "pnpm";
|
|
770
|
+
if (existsSync3(join3(projectPath, "yarn.lock"))) return "yarn";
|
|
771
|
+
if (existsSync3(join3(projectPath, "package-lock.json"))) return "npm";
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
function checkLockFile(projectPath) {
|
|
775
|
+
const checks = [];
|
|
776
|
+
const pm = detectPackageManager(projectPath);
|
|
777
|
+
if (pm) {
|
|
778
|
+
checks.push({
|
|
779
|
+
name: "lock_file_exists",
|
|
780
|
+
severity: "info",
|
|
781
|
+
message: `Package manager: ${pm}`
|
|
782
|
+
});
|
|
783
|
+
} else {
|
|
784
|
+
checks.push({
|
|
785
|
+
name: "lock_file_exists",
|
|
786
|
+
severity: "warning",
|
|
787
|
+
message: "No lock file found (pnpm-lock.yaml, yarn.lock, or package-lock.json)",
|
|
788
|
+
fix: "Run `npm install` to generate package-lock.json",
|
|
789
|
+
autoFixable: true
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
return checks;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/commands/check/checks/package-json.ts
|
|
796
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
797
|
+
import { join as join4 } from "path";
|
|
798
|
+
function readPackageJson(projectPath) {
|
|
799
|
+
const pkgPath = join4(projectPath, "package.json");
|
|
800
|
+
if (!existsSync4(pkgPath)) {
|
|
801
|
+
return { exists: false };
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
const raw = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
805
|
+
return {
|
|
806
|
+
exists: true,
|
|
807
|
+
scripts: raw.scripts,
|
|
808
|
+
dependencies: raw.dependencies,
|
|
809
|
+
devDependencies: raw.devDependencies
|
|
810
|
+
};
|
|
811
|
+
} catch {
|
|
812
|
+
return { exists: false };
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
function checkPackageJson(projectPath) {
|
|
816
|
+
const checks = [];
|
|
817
|
+
const pkg = readPackageJson(projectPath);
|
|
818
|
+
if (!pkg.exists) {
|
|
819
|
+
checks.push({
|
|
820
|
+
name: "package_json_exists",
|
|
821
|
+
severity: "error",
|
|
822
|
+
message: "package.json not found",
|
|
823
|
+
fix: "Run `npm init -y` to create one"
|
|
824
|
+
});
|
|
825
|
+
return checks;
|
|
826
|
+
}
|
|
827
|
+
if (!pkg.scripts?.dev) {
|
|
828
|
+
checks.push({
|
|
829
|
+
name: "scripts_dev",
|
|
830
|
+
severity: "error",
|
|
831
|
+
message: 'Missing "dev" script in package.json',
|
|
832
|
+
fix: 'Add a dev script (e.g., "dev": "vite" or "dev": "next dev")',
|
|
833
|
+
autoFixable: true
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
if (!pkg.scripts?.build) {
|
|
837
|
+
checks.push({
|
|
838
|
+
name: "scripts_build",
|
|
839
|
+
severity: "warning",
|
|
840
|
+
message: 'Missing "build" script in package.json \u2014 deployment may skip build step',
|
|
841
|
+
fix: 'Add a build script (e.g., "build": "vite build" or "build": "next build")',
|
|
842
|
+
autoFixable: true
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
if (pkg.scripts?.build) {
|
|
846
|
+
const build = pkg.scripts.build;
|
|
847
|
+
if (/^(?:vue-)?tsc\s.*&&/.test(build)) {
|
|
848
|
+
checks.push({
|
|
849
|
+
name: "build_tsc_prefix",
|
|
850
|
+
severity: "info",
|
|
851
|
+
message: `Build script starts with tsc: "${build}" \u2014 Rush will auto-strip the tsc check during build`
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return checks;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/commands/check/checks/port-env.ts
|
|
859
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
860
|
+
import { join as join5 } from "path";
|
|
861
|
+
var VITE_CONFIG_FILES2 = ["vite.config.ts", "vite.config.js"];
|
|
862
|
+
function readDevScript(projectPath) {
|
|
863
|
+
const pkgPath = join5(projectPath, "package.json");
|
|
864
|
+
if (!existsSync5(pkgPath)) return null;
|
|
865
|
+
try {
|
|
866
|
+
const raw = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
867
|
+
return raw.scripts?.dev ?? null;
|
|
868
|
+
} catch {
|
|
869
|
+
return null;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
function viteConfigReadsPort(projectPath) {
|
|
873
|
+
for (const file of VITE_CONFIG_FILES2) {
|
|
874
|
+
const filePath = join5(projectPath, file);
|
|
875
|
+
if (!existsSync5(filePath)) continue;
|
|
876
|
+
try {
|
|
877
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
878
|
+
if (/process\.env\.PORT/.test(content)) return true;
|
|
879
|
+
if (/env\s*\(\s*['"]PORT['"]\s*\)/.test(content)) return true;
|
|
880
|
+
} catch {
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
function devScriptReadsPort(devScript) {
|
|
886
|
+
if (/\$PORT|\$\{PORT\}|%PORT%/.test(devScript)) return true;
|
|
887
|
+
if (/--port\s+\$/.test(devScript)) return true;
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
function checkPortEnv(projectPath, framework) {
|
|
891
|
+
const checks = [];
|
|
892
|
+
if (framework === "nextjs") {
|
|
893
|
+
checks.push({
|
|
894
|
+
name: "port_env",
|
|
895
|
+
severity: "info",
|
|
896
|
+
message: "Next.js natively supports PORT env \u2014 no config change needed"
|
|
897
|
+
});
|
|
898
|
+
return checks;
|
|
899
|
+
}
|
|
900
|
+
if (framework === "vite") {
|
|
901
|
+
if (viteConfigReadsPort(projectPath)) {
|
|
902
|
+
checks.push({
|
|
903
|
+
name: "port_env",
|
|
904
|
+
severity: "info",
|
|
905
|
+
message: "Vite config reads process.env.PORT"
|
|
906
|
+
});
|
|
907
|
+
return checks;
|
|
908
|
+
}
|
|
909
|
+
const devScript2 = readDevScript(projectPath);
|
|
910
|
+
if (devScript2 && devScriptReadsPort(devScript2)) {
|
|
911
|
+
checks.push({
|
|
912
|
+
name: "port_env",
|
|
913
|
+
severity: "info",
|
|
914
|
+
message: "Dev script references PORT"
|
|
915
|
+
});
|
|
916
|
+
return checks;
|
|
917
|
+
}
|
|
918
|
+
checks.push({
|
|
919
|
+
name: "port_env",
|
|
920
|
+
severity: "error",
|
|
921
|
+
message: "Vite config does not read process.env.PORT \u2014 Rush Pod passes PORT=8000",
|
|
922
|
+
fix: 'Add `server: { port: parseInt(process.env.PORT || "5173") }` to vite.config.ts',
|
|
923
|
+
autoFixable: true
|
|
924
|
+
});
|
|
925
|
+
return checks;
|
|
926
|
+
}
|
|
927
|
+
const devScript = readDevScript(projectPath);
|
|
928
|
+
if (devScript && devScriptReadsPort(devScript)) {
|
|
929
|
+
checks.push({
|
|
930
|
+
name: "port_env",
|
|
931
|
+
severity: "info",
|
|
932
|
+
message: "Dev script references PORT"
|
|
933
|
+
});
|
|
934
|
+
} else {
|
|
935
|
+
checks.push({
|
|
936
|
+
name: "port_env",
|
|
937
|
+
severity: "error",
|
|
938
|
+
message: "Dev server must read PORT env var \u2014 Rush Pod passes PORT=8000",
|
|
939
|
+
fix: "Ensure your dev server reads process.env.PORT or accepts --port $PORT",
|
|
940
|
+
autoFixable: false
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
return checks;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// src/commands/check/index.ts
|
|
947
|
+
function severityIcon(severity) {
|
|
948
|
+
switch (severity) {
|
|
949
|
+
case "error":
|
|
950
|
+
return chalk2.red("\u2717");
|
|
951
|
+
case "warning":
|
|
952
|
+
return chalk2.yellow("!");
|
|
953
|
+
case "info":
|
|
954
|
+
return chalk2.dim("\xB7");
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
function displayResults(report) {
|
|
958
|
+
output.newline();
|
|
959
|
+
output.log(chalk2.bold(" Rush Deploy Check"));
|
|
960
|
+
output.log(
|
|
961
|
+
chalk2.dim(
|
|
962
|
+
` Framework: ${report.framework} | Package Manager: ${report.packageManager ?? "unknown"}`
|
|
963
|
+
)
|
|
964
|
+
);
|
|
965
|
+
output.newline();
|
|
966
|
+
const grouped = {
|
|
967
|
+
error: report.checks.filter((c) => c.severity === "error"),
|
|
968
|
+
warning: report.checks.filter((c) => c.severity === "warning"),
|
|
969
|
+
info: report.checks.filter((c) => c.severity === "info")
|
|
970
|
+
};
|
|
971
|
+
for (const [severity, checks] of Object.entries(grouped)) {
|
|
972
|
+
if (checks.length === 0) continue;
|
|
973
|
+
for (const check of checks) {
|
|
974
|
+
const icon = severityIcon(check.severity);
|
|
975
|
+
output.log(` ${icon} ${check.message}`);
|
|
976
|
+
if (check.fix && severity !== "info") {
|
|
977
|
+
output.log(chalk2.dim(` \u2192 ${check.fix}`));
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
output.newline();
|
|
982
|
+
const { errors, warnings } = report.summary;
|
|
983
|
+
const parts = [];
|
|
984
|
+
if (errors > 0) parts.push(chalk2.red(`${errors} errors`));
|
|
985
|
+
if (warnings > 0) parts.push(chalk2.yellow(`${warnings} warnings`));
|
|
986
|
+
if (errors === 0 && warnings === 0)
|
|
987
|
+
parts.push(chalk2.green("All checks passed"));
|
|
988
|
+
output.log(` ${parts.join(", ")}`);
|
|
989
|
+
if (report.deployable) {
|
|
990
|
+
output.newline();
|
|
991
|
+
output.log(
|
|
992
|
+
chalk2.green(" Ready to deploy! Run `rush-ai task deploy` to proceed.")
|
|
993
|
+
);
|
|
994
|
+
} else {
|
|
995
|
+
output.newline();
|
|
996
|
+
output.log(chalk2.red(" Fix the errors above before deploying."));
|
|
997
|
+
}
|
|
998
|
+
output.newline();
|
|
999
|
+
}
|
|
1000
|
+
function runChecks(projectPath) {
|
|
1001
|
+
const framework = detectFramework(projectPath);
|
|
1002
|
+
const packageManager = detectPackageManager(projectPath);
|
|
1003
|
+
const checks = [
|
|
1004
|
+
...checkPackageJson(projectPath),
|
|
1005
|
+
...checkFramework(projectPath),
|
|
1006
|
+
...checkLockFile(projectPath),
|
|
1007
|
+
...checkPortEnv(projectPath, framework)
|
|
1008
|
+
];
|
|
1009
|
+
const { checks: dbChecks, info: dbInfo } = checkDatabase(
|
|
1010
|
+
projectPath,
|
|
1011
|
+
framework
|
|
1012
|
+
);
|
|
1013
|
+
checks.push(...dbChecks);
|
|
1014
|
+
const errors = checks.filter((c) => c.severity === "error").length;
|
|
1015
|
+
const warnings = checks.filter((c) => c.severity === "warning").length;
|
|
1016
|
+
const info = checks.filter((c) => c.severity === "info").length;
|
|
1017
|
+
return {
|
|
1018
|
+
framework,
|
|
1019
|
+
packageManager,
|
|
1020
|
+
database: dbInfo,
|
|
1021
|
+
checks,
|
|
1022
|
+
summary: { errors, warnings, info },
|
|
1023
|
+
deployable: errors === 0
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
function registerCheckCommand(program) {
|
|
1027
|
+
program.command("check").description("Check if the current project is ready for Rush deployment").option("-p, --path <dir>", "Project directory to check", ".").action(async (opts) => {
|
|
1028
|
+
const projectPath = resolve(opts.path);
|
|
1029
|
+
const jsonMode = program.opts().json;
|
|
1030
|
+
const report = runChecks(projectPath);
|
|
1031
|
+
if (jsonMode) {
|
|
1032
|
+
output.log(JSON.stringify(report, null, 2));
|
|
1033
|
+
} else {
|
|
1034
|
+
displayResults(report);
|
|
1035
|
+
}
|
|
1036
|
+
if (!report.deployable) {
|
|
1037
|
+
process.exit(1);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
|
|
574
1042
|
// src/commands/completion/index.ts
|
|
575
|
-
import { existsSync } from "fs";
|
|
1043
|
+
import { existsSync as existsSync6 } from "fs";
|
|
576
1044
|
import { appendFile, mkdir, readFile, writeFile } from "fs/promises";
|
|
577
1045
|
import { homedir } from "os";
|
|
578
|
-
import { basename, dirname, join } from "path";
|
|
1046
|
+
import { basename, dirname, join as join6 } from "path";
|
|
579
1047
|
var MARKER_BEGIN = "###-begin-rush-ai-completion-###";
|
|
580
1048
|
var MARKER_END = "###-end-rush-ai-completion-###";
|
|
581
1049
|
var COMPLETION_SCRIPTS = {
|
|
@@ -644,11 +1112,11 @@ function getShellRcFile(shell) {
|
|
|
644
1112
|
const home = homedir();
|
|
645
1113
|
switch (shell) {
|
|
646
1114
|
case "bash":
|
|
647
|
-
return
|
|
1115
|
+
return existsSync6(join6(home, ".bashrc")) ? join6(home, ".bashrc") : join6(home, ".bash_profile");
|
|
648
1116
|
case "zsh":
|
|
649
|
-
return
|
|
1117
|
+
return join6(home, ".zshrc");
|
|
650
1118
|
case "fish":
|
|
651
|
-
return
|
|
1119
|
+
return join6(home, ".config", "fish", "completions", "rush-ai.fish");
|
|
652
1120
|
default:
|
|
653
1121
|
throw new Error(`Unsupported shell: ${shell}`);
|
|
654
1122
|
}
|
|
@@ -904,7 +1372,7 @@ function registerConfigCommand(program) {
|
|
|
904
1372
|
}
|
|
905
1373
|
|
|
906
1374
|
// src/commands/doctor/index.ts
|
|
907
|
-
import
|
|
1375
|
+
import chalk3 from "chalk";
|
|
908
1376
|
|
|
909
1377
|
// src/commands/doctor/checks/auth.ts
|
|
910
1378
|
var checkAuth = async () => {
|
|
@@ -1019,11 +1487,11 @@ var checkAuth = async () => {
|
|
|
1019
1487
|
};
|
|
1020
1488
|
|
|
1021
1489
|
// src/commands/doctor/checks/config.ts
|
|
1022
|
-
import { accessSync, constants, existsSync as
|
|
1490
|
+
import { accessSync, constants, existsSync as existsSync7, mkdirSync } from "fs";
|
|
1023
1491
|
var checkConfig = async () => {
|
|
1024
1492
|
const checks = [];
|
|
1025
1493
|
const configDir = getConfigDir();
|
|
1026
|
-
const dirExists =
|
|
1494
|
+
const dirExists = existsSync7(configDir);
|
|
1027
1495
|
let writable = false;
|
|
1028
1496
|
if (dirExists) {
|
|
1029
1497
|
try {
|
|
@@ -1273,18 +1741,18 @@ var checkEnvironment = async () => {
|
|
|
1273
1741
|
};
|
|
1274
1742
|
|
|
1275
1743
|
// src/commands/doctor/checks/plugins.ts
|
|
1276
|
-
import { existsSync as
|
|
1744
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
1277
1745
|
import { homedir as homedir2 } from "os";
|
|
1278
|
-
import { resolve } from "path";
|
|
1279
|
-
var PLUGINS_FILE =
|
|
1746
|
+
import { resolve as resolve2 } from "path";
|
|
1747
|
+
var PLUGINS_FILE = resolve2(homedir2(), ".rush", "plugins", "installed.json");
|
|
1280
1748
|
var MCP_CONFIG_PATHS = {
|
|
1281
|
-
"claude-code":
|
|
1282
|
-
cursor:
|
|
1749
|
+
"claude-code": resolve2(homedir2(), ".claude", "settings.json"),
|
|
1750
|
+
cursor: resolve2(homedir2(), ".cursor", "mcp.json")
|
|
1283
1751
|
};
|
|
1284
1752
|
function safeReadJson(filePath, fallback) {
|
|
1285
|
-
if (!
|
|
1753
|
+
if (!existsSync8(filePath)) return fallback;
|
|
1286
1754
|
try {
|
|
1287
|
-
return JSON.parse(
|
|
1755
|
+
return JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
1288
1756
|
} catch {
|
|
1289
1757
|
return fallback;
|
|
1290
1758
|
}
|
|
@@ -1314,7 +1782,7 @@ function checkMcpForPlugin(name, manifest) {
|
|
|
1314
1782
|
value: `Installed (v${manifest.version}), unknown type: ${manifest.type}`
|
|
1315
1783
|
};
|
|
1316
1784
|
}
|
|
1317
|
-
if (!
|
|
1785
|
+
if (!existsSync8(configPath)) {
|
|
1318
1786
|
return {
|
|
1319
1787
|
name: `plugin_${name}`,
|
|
1320
1788
|
group: "Plugins",
|
|
@@ -1328,7 +1796,7 @@ function checkMcpForPlugin(name, manifest) {
|
|
|
1328
1796
|
};
|
|
1329
1797
|
}
|
|
1330
1798
|
try {
|
|
1331
|
-
const config = JSON.parse(
|
|
1799
|
+
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
1332
1800
|
const servers = config.mcpServers;
|
|
1333
1801
|
if (!servers || !("rush" in servers)) {
|
|
1334
1802
|
return {
|
|
@@ -1431,17 +1899,17 @@ function getSummary(checks) {
|
|
|
1431
1899
|
function statusIcon(status) {
|
|
1432
1900
|
switch (status) {
|
|
1433
1901
|
case "pass":
|
|
1434
|
-
return
|
|
1902
|
+
return chalk3.green("\u2713");
|
|
1435
1903
|
case "warn":
|
|
1436
|
-
return
|
|
1904
|
+
return chalk3.yellow("!");
|
|
1437
1905
|
case "fail":
|
|
1438
|
-
return
|
|
1906
|
+
return chalk3.red("\u2717");
|
|
1439
1907
|
case "info":
|
|
1440
|
-
return
|
|
1908
|
+
return chalk3.dim("\xB7");
|
|
1441
1909
|
}
|
|
1442
1910
|
}
|
|
1443
1911
|
var LABEL_WIDTH = 20;
|
|
1444
|
-
function
|
|
1912
|
+
function displayResults2(checks) {
|
|
1445
1913
|
const groups = /* @__PURE__ */ new Map();
|
|
1446
1914
|
for (const check of checks) {
|
|
1447
1915
|
const existing = groups.get(check.group) ?? [];
|
|
@@ -1450,7 +1918,7 @@ function displayResults(checks) {
|
|
|
1450
1918
|
}
|
|
1451
1919
|
output.newline();
|
|
1452
1920
|
for (const [groupName, groupChecks] of groups) {
|
|
1453
|
-
output.log(` ${
|
|
1921
|
+
output.log(` ${chalk3.bold(groupName)}`);
|
|
1454
1922
|
for (const check of groupChecks) {
|
|
1455
1923
|
const icon = statusIcon(check.status);
|
|
1456
1924
|
const label = check.label.padEnd(LABEL_WIDTH);
|
|
@@ -1460,21 +1928,21 @@ function displayResults(checks) {
|
|
|
1460
1928
|
}
|
|
1461
1929
|
const summary = getSummary(checks);
|
|
1462
1930
|
const parts = [];
|
|
1463
|
-
parts.push(
|
|
1464
|
-
if (summary.warn > 0) parts.push(
|
|
1465
|
-
if (summary.fail > 0) parts.push(
|
|
1931
|
+
parts.push(chalk3.green(`${summary.pass} passed`));
|
|
1932
|
+
if (summary.warn > 0) parts.push(chalk3.yellow(`${summary.warn} warnings`));
|
|
1933
|
+
if (summary.fail > 0) parts.push(chalk3.red(`${summary.fail} failed`));
|
|
1466
1934
|
if (summary.warn === 0 && summary.fail === 0) {
|
|
1467
1935
|
parts.push("0 warnings");
|
|
1468
1936
|
parts.push("0 failed");
|
|
1469
1937
|
}
|
|
1470
|
-
output.log(` ${
|
|
1938
|
+
output.log(` ${chalk3.bold("Summary:")} ${parts.join(", ")}`);
|
|
1471
1939
|
if (summary.fail > 0 || summary.warn > 0) {
|
|
1472
1940
|
const fixable = checks.filter(
|
|
1473
1941
|
(c) => c.status !== "pass" && c.status !== "info" && c.autoFix
|
|
1474
1942
|
);
|
|
1475
1943
|
if (fixable.length > 0) {
|
|
1476
1944
|
output.log(
|
|
1477
|
-
|
|
1945
|
+
chalk3.dim(` Run \`rush-ai doctor --fix\` to attempt auto-fixes.`)
|
|
1478
1946
|
);
|
|
1479
1947
|
}
|
|
1480
1948
|
}
|
|
@@ -1528,7 +1996,7 @@ function registerDoctorCommand(program) {
|
|
|
1528
1996
|
if (jsonMode) {
|
|
1529
1997
|
outputJson(checks);
|
|
1530
1998
|
} else {
|
|
1531
|
-
|
|
1999
|
+
displayResults2(checks);
|
|
1532
2000
|
}
|
|
1533
2001
|
const hasFail = checks.some((c) => c.status === "fail");
|
|
1534
2002
|
if (hasFail) {
|
|
@@ -1677,34 +2145,34 @@ function registerMcpCommand(program) {
|
|
|
1677
2145
|
}
|
|
1678
2146
|
|
|
1679
2147
|
// src/commands/plugin/index.ts
|
|
1680
|
-
import { existsSync as
|
|
2148
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
1681
2149
|
import { homedir as homedir8 } from "os";
|
|
1682
|
-
import { resolve as
|
|
1683
|
-
import
|
|
2150
|
+
import { resolve as resolve7 } from "path";
|
|
2151
|
+
import chalk5 from "chalk";
|
|
1684
2152
|
|
|
1685
2153
|
// src/commands/plugin/adapters/claude-code.ts
|
|
1686
|
-
import { existsSync as
|
|
2154
|
+
import { existsSync as existsSync10 } from "fs";
|
|
1687
2155
|
import { homedir as homedir3 } from "os";
|
|
1688
|
-
import { resolve as
|
|
2156
|
+
import { resolve as resolve4 } from "path";
|
|
1689
2157
|
|
|
1690
2158
|
// src/commands/plugin/adapters/base.ts
|
|
1691
2159
|
import {
|
|
1692
2160
|
copyFileSync,
|
|
1693
|
-
existsSync as
|
|
2161
|
+
existsSync as existsSync9,
|
|
1694
2162
|
mkdirSync as mkdirSync2,
|
|
1695
|
-
readFileSync as
|
|
2163
|
+
readFileSync as readFileSync6,
|
|
1696
2164
|
writeFileSync as writeFileSync2
|
|
1697
2165
|
} from "fs";
|
|
1698
|
-
import { resolve as
|
|
2166
|
+
import { resolve as resolve3 } from "path";
|
|
1699
2167
|
var BaseJsonAdapter = class {
|
|
1700
2168
|
detect() {
|
|
1701
|
-
return
|
|
2169
|
+
return existsSync9(this.configDir);
|
|
1702
2170
|
}
|
|
1703
2171
|
readConfig() {
|
|
1704
2172
|
const file = this.configPath();
|
|
1705
|
-
if (!
|
|
2173
|
+
if (!existsSync9(file)) return {};
|
|
1706
2174
|
try {
|
|
1707
|
-
return JSON.parse(
|
|
2175
|
+
return JSON.parse(readFileSync6(file, "utf-8"));
|
|
1708
2176
|
} catch (err) {
|
|
1709
2177
|
throw new Error(
|
|
1710
2178
|
`Failed to parse ${file}: ${err instanceof Error ? err.message : "invalid JSON"}`
|
|
@@ -1734,16 +2202,16 @@ var BaseJsonAdapter = class {
|
|
|
1734
2202
|
}
|
|
1735
2203
|
writeConfig(config) {
|
|
1736
2204
|
const file = this.configPath();
|
|
1737
|
-
if (!
|
|
2205
|
+
if (!existsSync9(this.configDir)) {
|
|
1738
2206
|
mkdirSync2(this.configDir, { recursive: true });
|
|
1739
2207
|
}
|
|
1740
|
-
if (
|
|
2208
|
+
if (existsSync9(file)) {
|
|
1741
2209
|
copyFileSync(file, `${file}.bak`);
|
|
1742
2210
|
}
|
|
1743
2211
|
writeFileSync2(file, JSON.stringify(config, null, 2), "utf-8");
|
|
1744
2212
|
}
|
|
1745
2213
|
configPath() {
|
|
1746
|
-
return
|
|
2214
|
+
return resolve3(this.configDir, this.configFile);
|
|
1747
2215
|
}
|
|
1748
2216
|
};
|
|
1749
2217
|
|
|
@@ -1752,25 +2220,25 @@ var ClaudeCodeAdapter = class extends BaseJsonAdapter {
|
|
|
1752
2220
|
name = "claude-code";
|
|
1753
2221
|
description = "Claude Code integration (MCP config + SKILL.md + commands)";
|
|
1754
2222
|
version = "0.2.0";
|
|
1755
|
-
configDir =
|
|
2223
|
+
configDir = resolve4(homedir3(), ".claude");
|
|
1756
2224
|
configFile = "settings.json";
|
|
1757
2225
|
detect() {
|
|
1758
|
-
return
|
|
2226
|
+
return existsSync10(this.configDir);
|
|
1759
2227
|
}
|
|
1760
2228
|
};
|
|
1761
2229
|
|
|
1762
2230
|
// src/commands/plugin/adapters/cursor.ts
|
|
1763
|
-
import { existsSync as
|
|
2231
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1764
2232
|
import { homedir as homedir4 } from "os";
|
|
1765
|
-
import { resolve as
|
|
2233
|
+
import { resolve as resolve5 } from "path";
|
|
1766
2234
|
var CursorAdapter = class extends BaseJsonAdapter {
|
|
1767
2235
|
name = "cursor";
|
|
1768
2236
|
description = "Cursor integration (MCP config)";
|
|
1769
2237
|
version = "0.2.0";
|
|
1770
|
-
configDir =
|
|
2238
|
+
configDir = resolve5(homedir4(), ".cursor");
|
|
1771
2239
|
configFile = "mcp.json";
|
|
1772
2240
|
detect() {
|
|
1773
|
-
return
|
|
2241
|
+
return existsSync11(this.configDir);
|
|
1774
2242
|
}
|
|
1775
2243
|
};
|
|
1776
2244
|
|
|
@@ -1801,11 +2269,11 @@ function getAdapterDescriptions() {
|
|
|
1801
2269
|
import { createHash } from "crypto";
|
|
1802
2270
|
import {
|
|
1803
2271
|
copyFileSync as copyFileSync2,
|
|
1804
|
-
existsSync as
|
|
2272
|
+
existsSync as existsSync12,
|
|
1805
2273
|
lstatSync,
|
|
1806
2274
|
mkdirSync as mkdirSync3,
|
|
1807
2275
|
readdirSync,
|
|
1808
|
-
readFileSync as
|
|
2276
|
+
readFileSync as readFileSync7,
|
|
1809
2277
|
readlinkSync,
|
|
1810
2278
|
rmSync,
|
|
1811
2279
|
symlinkSync,
|
|
@@ -1813,24 +2281,24 @@ import {
|
|
|
1813
2281
|
writeFileSync as writeFileSync3
|
|
1814
2282
|
} from "fs";
|
|
1815
2283
|
import { homedir as homedir5 } from "os";
|
|
1816
|
-
import { dirname as dirname2, join as
|
|
2284
|
+
import { dirname as dirname2, join as join7, relative, resolve as resolve6 } from "path";
|
|
1817
2285
|
var ASSET_SCHEMA_VERSION = 1;
|
|
1818
|
-
var PLUGINS_BASE =
|
|
2286
|
+
var PLUGINS_BASE = resolve6(homedir5(), ".rush", "plugins");
|
|
1819
2287
|
function getAssetManifestPath(pluginName) {
|
|
1820
|
-
return
|
|
2288
|
+
return resolve6(PLUGINS_BASE, pluginName, "asset-manifest.json");
|
|
1821
2289
|
}
|
|
1822
2290
|
function getAssetStorePath(pluginName) {
|
|
1823
|
-
return
|
|
2291
|
+
return resolve6(PLUGINS_BASE, pluginName, "assets");
|
|
1824
2292
|
}
|
|
1825
2293
|
function computeChecksum(filePath) {
|
|
1826
|
-
const content =
|
|
2294
|
+
const content = readFileSync7(filePath);
|
|
1827
2295
|
return createHash("sha256").update(content).digest("hex");
|
|
1828
2296
|
}
|
|
1829
2297
|
function loadAssetManifest(pluginName) {
|
|
1830
2298
|
const manifestPath = getAssetManifestPath(pluginName);
|
|
1831
|
-
if (!
|
|
2299
|
+
if (!existsSync12(manifestPath)) return null;
|
|
1832
2300
|
try {
|
|
1833
|
-
return JSON.parse(
|
|
2301
|
+
return JSON.parse(readFileSync7(manifestPath, "utf-8"));
|
|
1834
2302
|
} catch {
|
|
1835
2303
|
return null;
|
|
1836
2304
|
}
|
|
@@ -1844,10 +2312,10 @@ function installAssets(options) {
|
|
|
1844
2312
|
const existing = loadAssetManifest(pluginName);
|
|
1845
2313
|
const sourceFiles = collectFiles(sourceDir);
|
|
1846
2314
|
for (const relPath of sourceFiles) {
|
|
1847
|
-
const srcFile =
|
|
1848
|
-
const destFile =
|
|
2315
|
+
const srcFile = resolve6(sourceDir, relPath);
|
|
2316
|
+
const destFile = resolve6(storePath, relPath);
|
|
1849
2317
|
const newChecksum = computeChecksum(srcFile);
|
|
1850
|
-
if (
|
|
2318
|
+
if (existsSync12(destFile) && !force && existing) {
|
|
1851
2319
|
const existingEntry = existing.files.find((f) => f.path === relPath);
|
|
1852
2320
|
if (existingEntry) {
|
|
1853
2321
|
const currentChecksum = computeChecksum(destFile);
|
|
@@ -1872,8 +2340,8 @@ function installAssets(options) {
|
|
|
1872
2340
|
type: "file"
|
|
1873
2341
|
});
|
|
1874
2342
|
}
|
|
1875
|
-
const skillTarget =
|
|
1876
|
-
if (!
|
|
2343
|
+
const skillTarget = resolve6(homedir5(), ".claude", "skills", "rush-task");
|
|
2344
|
+
if (!existsSync12(skillTarget)) {
|
|
1877
2345
|
mkdirSync3(dirname2(skillTarget), { recursive: true });
|
|
1878
2346
|
try {
|
|
1879
2347
|
symlinkSync(storePath, skillTarget, "dir");
|
|
@@ -1893,7 +2361,7 @@ function installAssets(options) {
|
|
|
1893
2361
|
const stat = lstatSync(skillTarget);
|
|
1894
2362
|
if (stat.isSymbolicLink()) {
|
|
1895
2363
|
const linkTarget = readlinkSync(skillTarget);
|
|
1896
|
-
if (linkTarget === storePath ||
|
|
2364
|
+
if (linkTarget === storePath || resolve6(linkTarget) === storePath) {
|
|
1897
2365
|
files.push({
|
|
1898
2366
|
path: "_symlink_skill",
|
|
1899
2367
|
checksum: "",
|
|
@@ -1925,11 +2393,11 @@ function installAssets(options) {
|
|
|
1925
2393
|
return manifest;
|
|
1926
2394
|
}
|
|
1927
2395
|
var ALLOWED_DELETE_PREFIXES = [
|
|
1928
|
-
|
|
1929
|
-
|
|
2396
|
+
resolve6(homedir5(), ".rush", "plugins"),
|
|
2397
|
+
resolve6(homedir5(), ".claude", "skills")
|
|
1930
2398
|
];
|
|
1931
2399
|
function isPathAllowed(targetPath) {
|
|
1932
|
-
const resolved =
|
|
2400
|
+
const resolved = resolve6(targetPath);
|
|
1933
2401
|
return ALLOWED_DELETE_PREFIXES.some(
|
|
1934
2402
|
(prefix) => resolved.startsWith(`${prefix}/`)
|
|
1935
2403
|
);
|
|
@@ -1944,7 +2412,7 @@ function uninstallAssets(pluginName) {
|
|
|
1944
2412
|
return;
|
|
1945
2413
|
}
|
|
1946
2414
|
for (const entry of manifest.files) {
|
|
1947
|
-
if (!
|
|
2415
|
+
if (!existsSync12(entry.target)) continue;
|
|
1948
2416
|
if (!isPathAllowed(entry.target)) {
|
|
1949
2417
|
output.warn(
|
|
1950
2418
|
` Refusing to delete ${entry.target}: outside allowed directories`
|
|
@@ -1974,7 +2442,7 @@ function uninstallAssets(pluginName) {
|
|
|
1974
2442
|
}
|
|
1975
2443
|
}
|
|
1976
2444
|
const manifestPath = getAssetManifestPath(pluginName);
|
|
1977
|
-
if (
|
|
2445
|
+
if (existsSync12(manifestPath)) {
|
|
1978
2446
|
rmSync(manifestPath);
|
|
1979
2447
|
}
|
|
1980
2448
|
const storePath = getAssetStorePath(pluginName);
|
|
@@ -1990,7 +2458,7 @@ function collectFiles(dir, base) {
|
|
|
1990
2458
|
const files = [];
|
|
1991
2459
|
const baseDir = base ?? dir;
|
|
1992
2460
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1993
|
-
const fullPath =
|
|
2461
|
+
const fullPath = join7(dir, entry.name);
|
|
1994
2462
|
if (entry.isDirectory()) {
|
|
1995
2463
|
files.push(...collectFiles(fullPath, baseDir));
|
|
1996
2464
|
} else if (entry.isFile()) {
|
|
@@ -2001,14 +2469,14 @@ function collectFiles(dir, base) {
|
|
|
2001
2469
|
}
|
|
2002
2470
|
|
|
2003
2471
|
// src/commands/plugin/doctor.ts
|
|
2004
|
-
import { existsSync as
|
|
2472
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
2005
2473
|
import { homedir as homedir6 } from "os";
|
|
2006
2474
|
import path from "path";
|
|
2007
2475
|
function findCliInPath() {
|
|
2008
2476
|
const pathDirs = (process.env.PATH || "").split(path.delimiter);
|
|
2009
2477
|
for (const dir of pathDirs) {
|
|
2010
2478
|
const candidate = path.join(dir, "rush-ai");
|
|
2011
|
-
if (
|
|
2479
|
+
if (existsSync13(candidate)) return candidate;
|
|
2012
2480
|
}
|
|
2013
2481
|
return null;
|
|
2014
2482
|
}
|
|
@@ -2103,7 +2571,7 @@ function checkMcpConfig(ctx) {
|
|
|
2103
2571
|
detail: `Unknown plugin type: ${type2}`
|
|
2104
2572
|
};
|
|
2105
2573
|
}
|
|
2106
|
-
if (!
|
|
2574
|
+
if (!existsSync13(configPath)) {
|
|
2107
2575
|
return {
|
|
2108
2576
|
name: "mcp_config",
|
|
2109
2577
|
label: "MCP config",
|
|
@@ -2113,7 +2581,7 @@ function checkMcpConfig(ctx) {
|
|
|
2113
2581
|
};
|
|
2114
2582
|
}
|
|
2115
2583
|
try {
|
|
2116
|
-
const config = JSON.parse(
|
|
2584
|
+
const config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
2117
2585
|
const servers = config.mcpServers;
|
|
2118
2586
|
if (servers && "rush" in servers) {
|
|
2119
2587
|
const rushServer = servers.rush;
|
|
@@ -2188,10 +2656,10 @@ async function runDoctorChecks(ctx) {
|
|
|
2188
2656
|
}
|
|
2189
2657
|
|
|
2190
2658
|
// src/commands/plugin/preflight.ts
|
|
2191
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
2659
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync14, mkdirSync as mkdirSync4 } from "fs";
|
|
2192
2660
|
import { homedir as homedir7 } from "os";
|
|
2193
2661
|
import path2 from "path";
|
|
2194
|
-
import
|
|
2662
|
+
import chalk4 from "chalk";
|
|
2195
2663
|
async function runPreflightChecks(options) {
|
|
2196
2664
|
const results = [];
|
|
2197
2665
|
try {
|
|
@@ -2233,7 +2701,7 @@ async function runPreflightChecks(options) {
|
|
|
2233
2701
|
}
|
|
2234
2702
|
const rushDir = path2.resolve(homedir7(), ".rush");
|
|
2235
2703
|
try {
|
|
2236
|
-
if (!
|
|
2704
|
+
if (!existsSync14(rushDir)) {
|
|
2237
2705
|
mkdirSync4(rushDir, { recursive: true });
|
|
2238
2706
|
}
|
|
2239
2707
|
accessSync2(rushDir, constants2.W_OK);
|
|
@@ -2254,7 +2722,7 @@ async function runPreflightChecks(options) {
|
|
|
2254
2722
|
} else {
|
|
2255
2723
|
output.info("Preflight checks...");
|
|
2256
2724
|
for (const r of results) {
|
|
2257
|
-
const icon = r.status === "pass" ?
|
|
2725
|
+
const icon = r.status === "pass" ? chalk4.green("[PASS]") : r.status === "warn" ? chalk4.yellow("[WARN]") : chalk4.red("[FAIL]");
|
|
2258
2726
|
output.log(` ${icon} ${r.name}: ${r.detail}`);
|
|
2259
2727
|
}
|
|
2260
2728
|
}
|
|
@@ -2263,7 +2731,7 @@ async function runPreflightChecks(options) {
|
|
|
2263
2731
|
|
|
2264
2732
|
// src/commands/plugin/verify.ts
|
|
2265
2733
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
2266
|
-
import { existsSync as
|
|
2734
|
+
import { existsSync as existsSync15 } from "fs";
|
|
2267
2735
|
async function runVerification(options) {
|
|
2268
2736
|
const results = [];
|
|
2269
2737
|
results.push(checkConfigFile(options.adapter));
|
|
@@ -2274,7 +2742,7 @@ async function runVerification(options) {
|
|
|
2274
2742
|
}
|
|
2275
2743
|
function checkConfigFile(adapter) {
|
|
2276
2744
|
const configFile = adapter.configPath();
|
|
2277
|
-
if (!
|
|
2745
|
+
if (!existsSync15(configFile)) {
|
|
2278
2746
|
return {
|
|
2279
2747
|
name: "config_file",
|
|
2280
2748
|
label: "IDE config",
|
|
@@ -2399,12 +2867,12 @@ function checkAuthStatus() {
|
|
|
2399
2867
|
|
|
2400
2868
|
// src/commands/plugin/index.ts
|
|
2401
2869
|
var CURRENT_SCHEMA_VERSION = 2;
|
|
2402
|
-
var PLUGINS_DIR =
|
|
2403
|
-
var PLUGINS_FILE2 =
|
|
2870
|
+
var PLUGINS_DIR = resolve7(homedir8(), ".rush", "plugins");
|
|
2871
|
+
var PLUGINS_FILE2 = resolve7(PLUGINS_DIR, "installed.json");
|
|
2404
2872
|
function safeReadJson2(filePath, fallback) {
|
|
2405
|
-
if (!
|
|
2873
|
+
if (!existsSync16(filePath)) return fallback;
|
|
2406
2874
|
try {
|
|
2407
|
-
return JSON.parse(
|
|
2875
|
+
return JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
2408
2876
|
} catch {
|
|
2409
2877
|
return fallback;
|
|
2410
2878
|
}
|
|
@@ -2424,7 +2892,7 @@ function loadInstalledPlugins(autoMigrate = true) {
|
|
|
2424
2892
|
return raw;
|
|
2425
2893
|
}
|
|
2426
2894
|
function saveInstalledPlugins(data) {
|
|
2427
|
-
if (!
|
|
2895
|
+
if (!existsSync16(PLUGINS_DIR)) {
|
|
2428
2896
|
mkdirSync5(PLUGINS_DIR, { recursive: true });
|
|
2429
2897
|
}
|
|
2430
2898
|
writeFileSync4(PLUGINS_FILE2, JSON.stringify(data, null, 2), "utf-8");
|
|
@@ -2455,21 +2923,21 @@ function resolvePluginAssetsDir() {
|
|
|
2455
2923
|
);
|
|
2456
2924
|
return null;
|
|
2457
2925
|
}
|
|
2458
|
-
const bundled =
|
|
2459
|
-
if (
|
|
2460
|
-
const monorepo =
|
|
2461
|
-
if (
|
|
2926
|
+
const bundled = resolve7(baseDir, "plugin-assets");
|
|
2927
|
+
if (existsSync16(bundled)) return bundled;
|
|
2928
|
+
const monorepo = resolve7(baseDir, "..", "..", "..", "..", "rush-plugin");
|
|
2929
|
+
if (existsSync16(monorepo)) return monorepo;
|
|
2462
2930
|
return null;
|
|
2463
2931
|
}
|
|
2464
2932
|
function getCliVersion() {
|
|
2465
2933
|
try {
|
|
2466
|
-
const pkgPath =
|
|
2934
|
+
const pkgPath = resolve7(
|
|
2467
2935
|
import.meta.dirname ?? __dirname,
|
|
2468
2936
|
"..",
|
|
2469
2937
|
"..",
|
|
2470
2938
|
"package.json"
|
|
2471
2939
|
);
|
|
2472
|
-
const pkg = JSON.parse(
|
|
2940
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
2473
2941
|
return pkg.version ?? "0.2.0";
|
|
2474
2942
|
} catch {
|
|
2475
2943
|
return "0.2.0";
|
|
@@ -2747,7 +3215,7 @@ function registerPluginCommand(program) {
|
|
|
2747
3215
|
let warn = 0;
|
|
2748
3216
|
let fail = 0;
|
|
2749
3217
|
for (const check of checks) {
|
|
2750
|
-
const icon = check.status === "pass" ?
|
|
3218
|
+
const icon = check.status === "pass" ? chalk5.green("[PASS]") : check.status === "warn" ? chalk5.yellow("[WARN]") : chalk5.red("[FAIL]");
|
|
2751
3219
|
output.log(` ${icon} ${check.label}: ${check.detail}`);
|
|
2752
3220
|
if (check.fix) {
|
|
2753
3221
|
output.dim(` Fix: ${check.fix}`);
|
|
@@ -2768,7 +3236,7 @@ function registerPluginCommand(program) {
|
|
|
2768
3236
|
}
|
|
2769
3237
|
function printVerifyResults(results) {
|
|
2770
3238
|
for (const r of results) {
|
|
2771
|
-
const icon = r.status === "pass" ?
|
|
3239
|
+
const icon = r.status === "pass" ? chalk5.green("[PASS]") : r.status === "warn" ? chalk5.yellow("[WARN]") : chalk5.red("[FAIL]");
|
|
2772
3240
|
output.log(` ${icon} ${r.label}: ${r.detail}`);
|
|
2773
3241
|
if (r.fix) {
|
|
2774
3242
|
output.dim(` Fix: ${r.fix}`);
|
|
@@ -2784,17 +3252,17 @@ import { Readable } from "stream";
|
|
|
2784
3252
|
import { pipeline } from "stream/promises";
|
|
2785
3253
|
|
|
2786
3254
|
// src/output/diff.ts
|
|
2787
|
-
import
|
|
3255
|
+
import chalk6 from "chalk";
|
|
2788
3256
|
function containsDiff(text) {
|
|
2789
3257
|
return /^@@\s+-\d+/m.test(text) || /^---\s+a\//m.test(text);
|
|
2790
3258
|
}
|
|
2791
3259
|
function colorizeDiff(text) {
|
|
2792
3260
|
return text.split("\n").map((line) => {
|
|
2793
3261
|
if (line.startsWith("+++") || line.startsWith("---"))
|
|
2794
|
-
return
|
|
2795
|
-
if (line.startsWith("+")) return
|
|
2796
|
-
if (line.startsWith("-")) return
|
|
2797
|
-
if (line.startsWith("@@")) return
|
|
3262
|
+
return chalk6.bold(line);
|
|
3263
|
+
if (line.startsWith("+")) return chalk6.green(line);
|
|
3264
|
+
if (line.startsWith("-")) return chalk6.red(line);
|
|
3265
|
+
if (line.startsWith("@@")) return chalk6.cyan(line);
|
|
2798
3266
|
return line;
|
|
2799
3267
|
}).join("\n");
|
|
2800
3268
|
}
|
|
@@ -2817,18 +3285,228 @@ async function readStdinIfPiped() {
|
|
|
2817
3285
|
if (process.stdin.isTTY) {
|
|
2818
3286
|
return null;
|
|
2819
3287
|
}
|
|
2820
|
-
return new Promise((
|
|
3288
|
+
return new Promise((resolve9, reject) => {
|
|
2821
3289
|
const chunks = [];
|
|
2822
3290
|
process.stdin.on("data", (chunk) => {
|
|
2823
3291
|
chunks.push(chunk);
|
|
2824
3292
|
});
|
|
2825
3293
|
process.stdin.on("end", () => {
|
|
2826
|
-
|
|
3294
|
+
resolve9(Buffer.concat(chunks).toString("utf-8").trim());
|
|
2827
3295
|
});
|
|
2828
3296
|
process.stdin.on("error", reject);
|
|
2829
3297
|
});
|
|
2830
3298
|
}
|
|
2831
3299
|
|
|
3300
|
+
// src/commands/task/deploy.ts
|
|
3301
|
+
import { resolve as resolve8 } from "path";
|
|
3302
|
+
import chalk7 from "chalk";
|
|
3303
|
+
|
|
3304
|
+
// src/util/git.ts
|
|
3305
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
3306
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3307
|
+
import { join as join8 } from "path";
|
|
3308
|
+
function isGitRepo(projectPath) {
|
|
3309
|
+
return existsSync17(join8(projectPath, ".git"));
|
|
3310
|
+
}
|
|
3311
|
+
function gitInit(projectPath) {
|
|
3312
|
+
execFileSync3("git", ["init"], { cwd: projectPath, stdio: "pipe" });
|
|
3313
|
+
}
|
|
3314
|
+
function hasUncommittedChanges(projectPath) {
|
|
3315
|
+
try {
|
|
3316
|
+
const status = execFileSync3("git", ["status", "--porcelain"], {
|
|
3317
|
+
cwd: projectPath,
|
|
3318
|
+
encoding: "utf-8"
|
|
3319
|
+
});
|
|
3320
|
+
return status.trim().length > 0;
|
|
3321
|
+
} catch {
|
|
3322
|
+
return false;
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
function gitAddAndCommit(projectPath, message) {
|
|
3326
|
+
execFileSync3("git", ["add", "-A"], { cwd: projectPath, stdio: "pipe" });
|
|
3327
|
+
execFileSync3("git", ["commit", "-m", message, "--allow-empty"], {
|
|
3328
|
+
cwd: projectPath,
|
|
3329
|
+
stdio: "pipe",
|
|
3330
|
+
env: {
|
|
3331
|
+
...process.env,
|
|
3332
|
+
GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME || "rush-ai",
|
|
3333
|
+
GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL || "rush-ai@rush.dev",
|
|
3334
|
+
GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME || "rush-ai",
|
|
3335
|
+
GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL || "rush-ai@rush.dev"
|
|
3336
|
+
}
|
|
3337
|
+
});
|
|
3338
|
+
}
|
|
3339
|
+
function gitPushUrl(projectPath, url) {
|
|
3340
|
+
try {
|
|
3341
|
+
execFileSync3("git", ["push", url, "HEAD:main", "--force"], {
|
|
3342
|
+
cwd: projectPath,
|
|
3343
|
+
stdio: "pipe",
|
|
3344
|
+
timeout: 3e5,
|
|
3345
|
+
maxBuffer: 10 * 1024 * 1024
|
|
3346
|
+
});
|
|
3347
|
+
return { success: true, stderr: "" };
|
|
3348
|
+
} catch (err) {
|
|
3349
|
+
const rawStderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr) : "unknown error";
|
|
3350
|
+
return { success: false, stderr: sanitizeGitOutput(rawStderr) };
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
function sanitizeGitOutput(text) {
|
|
3354
|
+
return text.replace(/https?:\/\/[^@]*@/g, "https://***@");
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
// src/commands/task/deploy.ts
|
|
3358
|
+
function sleep(ms) {
|
|
3359
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
3360
|
+
}
|
|
3361
|
+
function maskToken2(url) {
|
|
3362
|
+
return url.replace(/:([^@]{4})[^@]*@/, ":$1****@");
|
|
3363
|
+
}
|
|
3364
|
+
function registerDeploySubcommand(task, program) {
|
|
3365
|
+
task.command("deploy").description("Deploy a local project to Rush platform").option("-n, --name <name>", "Project name").option("-p, --path <dir>", "Project directory", ".").action(async (opts) => {
|
|
3366
|
+
requireAuth();
|
|
3367
|
+
const format = resolveFormat(program.opts());
|
|
3368
|
+
const client = createClient();
|
|
3369
|
+
const projectPath = resolve8(opts.path);
|
|
3370
|
+
if (format !== "json") {
|
|
3371
|
+
output.info("Checking project readiness...");
|
|
3372
|
+
}
|
|
3373
|
+
const report = runChecks(projectPath);
|
|
3374
|
+
if (!report.deployable) {
|
|
3375
|
+
if (format === "json") {
|
|
3376
|
+
output.log(
|
|
3377
|
+
JSON.stringify(
|
|
3378
|
+
{ error: "Project has errors that must be fixed", report },
|
|
3379
|
+
null,
|
|
3380
|
+
2
|
|
3381
|
+
)
|
|
3382
|
+
);
|
|
3383
|
+
} else {
|
|
3384
|
+
output.error(
|
|
3385
|
+
"Project has errors that must be fixed before deploying."
|
|
3386
|
+
);
|
|
3387
|
+
output.dim("Run `rush-ai check` for details.");
|
|
3388
|
+
}
|
|
3389
|
+
process.exit(1);
|
|
3390
|
+
}
|
|
3391
|
+
if (format !== "json") {
|
|
3392
|
+
output.info("Creating Rush project...");
|
|
3393
|
+
}
|
|
3394
|
+
const projectName = opts.name || `deploy-${Date.now().toString(36)}`;
|
|
3395
|
+
const { data } = await client.post(
|
|
3396
|
+
"/api/projects/deploy-cli",
|
|
3397
|
+
{
|
|
3398
|
+
name: projectName,
|
|
3399
|
+
framework: report.framework,
|
|
3400
|
+
packageManager: report.packageManager
|
|
3401
|
+
}
|
|
3402
|
+
);
|
|
3403
|
+
if (!data.success || !data.gitPushUrl) {
|
|
3404
|
+
throw new RushError(
|
|
3405
|
+
"Failed to create project \u2014 no git push URL returned"
|
|
3406
|
+
);
|
|
3407
|
+
}
|
|
3408
|
+
if (format !== "json") {
|
|
3409
|
+
output.success(`Project created: ${data.projectId}`);
|
|
3410
|
+
}
|
|
3411
|
+
if (format !== "json") {
|
|
3412
|
+
output.info("Pushing code to Rush...");
|
|
3413
|
+
}
|
|
3414
|
+
if (!isGitRepo(projectPath)) {
|
|
3415
|
+
gitInit(projectPath);
|
|
3416
|
+
}
|
|
3417
|
+
if (hasUncommittedChanges(projectPath)) {
|
|
3418
|
+
gitAddAndCommit(projectPath, "deploy to rush");
|
|
3419
|
+
}
|
|
3420
|
+
const pushResult = gitPushUrl(projectPath, data.gitPushUrl);
|
|
3421
|
+
if (!pushResult.success) {
|
|
3422
|
+
if (format === "json") {
|
|
3423
|
+
output.log(
|
|
3424
|
+
JSON.stringify(
|
|
3425
|
+
{
|
|
3426
|
+
error: "Git push failed",
|
|
3427
|
+
details: pushResult.stderr,
|
|
3428
|
+
projectId: data.projectId
|
|
3429
|
+
},
|
|
3430
|
+
null,
|
|
3431
|
+
2
|
|
3432
|
+
)
|
|
3433
|
+
);
|
|
3434
|
+
} else {
|
|
3435
|
+
output.error("Git push failed:");
|
|
3436
|
+
output.log(pushResult.stderr);
|
|
3437
|
+
output.dim(
|
|
3438
|
+
"Check your VPN connection and network. The project was created but code was not pushed."
|
|
3439
|
+
);
|
|
3440
|
+
output.dim(`Project ID: ${data.projectId}`);
|
|
3441
|
+
}
|
|
3442
|
+
process.exit(1);
|
|
3443
|
+
}
|
|
3444
|
+
if (format !== "json") {
|
|
3445
|
+
output.success("Code pushed successfully");
|
|
3446
|
+
}
|
|
3447
|
+
if (format !== "json") {
|
|
3448
|
+
output.info("Initializing project on Rush...");
|
|
3449
|
+
}
|
|
3450
|
+
try {
|
|
3451
|
+
await client.post(
|
|
3452
|
+
`/api/projects/${encodeURIComponent(data.projectId)}/init-clone`,
|
|
3453
|
+
{
|
|
3454
|
+
projectId: data.projectId,
|
|
3455
|
+
sourceProjectId: data.projectId
|
|
3456
|
+
}
|
|
3457
|
+
);
|
|
3458
|
+
} catch {
|
|
3459
|
+
if (format !== "json") {
|
|
3460
|
+
output.warn(
|
|
3461
|
+
"Init-clone request failed \u2014 the pod may auto-initialize on startup"
|
|
3462
|
+
);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
if (format !== "json") {
|
|
3466
|
+
output.info("Waiting for project to be ready...");
|
|
3467
|
+
}
|
|
3468
|
+
const maxWait = 15e4;
|
|
3469
|
+
const pollInterval = 3e3;
|
|
3470
|
+
const startTime = Date.now();
|
|
3471
|
+
let ready = false;
|
|
3472
|
+
while (Date.now() - startTime < maxWait) {
|
|
3473
|
+
try {
|
|
3474
|
+
const { data: readyData } = await client.get(
|
|
3475
|
+
`/api/projects/${encodeURIComponent(data.projectId)}/pod-ready`
|
|
3476
|
+
);
|
|
3477
|
+
if (readyData?.data?.ready) {
|
|
3478
|
+
ready = true;
|
|
3479
|
+
break;
|
|
3480
|
+
}
|
|
3481
|
+
} catch {
|
|
3482
|
+
}
|
|
3483
|
+
await sleep(pollInterval);
|
|
3484
|
+
}
|
|
3485
|
+
const result = {
|
|
3486
|
+
projectId: data.projectId,
|
|
3487
|
+
previewUrl: data.podPreviewUrl,
|
|
3488
|
+
chatUrl: data.podChatUrl,
|
|
3489
|
+
status: ready ? "ready" : "timeout",
|
|
3490
|
+
gitRemote: maskToken2(data.gitPushUrl)
|
|
3491
|
+
};
|
|
3492
|
+
if (format === "json") {
|
|
3493
|
+
output.log(JSON.stringify(result, null, 2));
|
|
3494
|
+
} else {
|
|
3495
|
+
output.newline();
|
|
3496
|
+
if (ready) {
|
|
3497
|
+
output.success("Deployment complete!");
|
|
3498
|
+
} else {
|
|
3499
|
+
output.warn("Pod is still starting up \u2014 it may take another minute.");
|
|
3500
|
+
}
|
|
3501
|
+
output.log(` ${chalk7.bold("Project ID:")} ${result.projectId}`);
|
|
3502
|
+
output.log(` ${chalk7.bold("Preview:")} ${result.previewUrl}`);
|
|
3503
|
+
output.log(` ${chalk7.bold("Chat:")} ${result.chatUrl}`);
|
|
3504
|
+
output.log(` ${chalk7.bold("Git remote:")} ${result.gitRemote}`);
|
|
3505
|
+
output.newline();
|
|
3506
|
+
}
|
|
3507
|
+
});
|
|
3508
|
+
}
|
|
3509
|
+
|
|
2832
3510
|
// src/commands/task/index.ts
|
|
2833
3511
|
var VALID_CATEGORIES = ["code", "image", "document", "other"];
|
|
2834
3512
|
function sanitizeFilePath(filePath, outputDir) {
|
|
@@ -2869,6 +3547,7 @@ function buildCategorySummary(files) {
|
|
|
2869
3547
|
}
|
|
2870
3548
|
function registerTaskCommand(program) {
|
|
2871
3549
|
const task = program.command("task").description("Create and manage tasks");
|
|
3550
|
+
registerDeploySubcommand(task, program);
|
|
2872
3551
|
task.command("create").description("Create a task asynchronously").requiredOption("-a, --agent <name>", "Agent name to execute the task").option("-p, --prompt <text>", "Task prompt").option("--skills <skills>", "Comma-separated skills to add", commaSplit).option("--mcp <servers>", "Comma-separated MCP servers to add", commaSplit).action(
|
|
2873
3552
|
async (options) => {
|
|
2874
3553
|
requireAuth();
|
|
@@ -3253,6 +3932,7 @@ function registerCommands(program) {
|
|
|
3253
3932
|
registerAuthCommand(program);
|
|
3254
3933
|
registerAgentCommand(program);
|
|
3255
3934
|
registerTaskCommand(program);
|
|
3935
|
+
registerCheckCommand(program);
|
|
3256
3936
|
registerMcpCommand(program);
|
|
3257
3937
|
registerPluginCommand(program);
|
|
3258
3938
|
registerCompletionCommand(program);
|
|
@@ -3263,7 +3943,7 @@ function registerCommands(program) {
|
|
|
3263
3943
|
// src/util/update-check.ts
|
|
3264
3944
|
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3265
3945
|
import { homedir as homedir9 } from "os";
|
|
3266
|
-
import { dirname as dirname3, join as
|
|
3946
|
+
import { dirname as dirname3, join as join9 } from "path";
|
|
3267
3947
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3268
3948
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
3269
3949
|
var REGISTRY_URL = "https://registry.npmjs.org/rush-ai/latest";
|
|
@@ -3292,8 +3972,8 @@ async function writeLastCheck(checkFile) {
|
|
|
3292
3972
|
}
|
|
3293
3973
|
}
|
|
3294
3974
|
async function checkForUpdate(currentVersion) {
|
|
3295
|
-
const rushDir =
|
|
3296
|
-
const checkFile =
|
|
3975
|
+
const rushDir = join9(homedir9(), ".rush");
|
|
3976
|
+
const checkFile = join9(rushDir, "update-check.json");
|
|
3297
3977
|
try {
|
|
3298
3978
|
if (process.env.CI) return;
|
|
3299
3979
|
let lastCheck = 0;
|
|
@@ -3322,28 +4002,28 @@ async function checkForUpdate(currentVersion) {
|
|
|
3322
4002
|
|
|
3323
4003
|
// src/index.ts
|
|
3324
4004
|
var BANNER = `
|
|
3325
|
-
${
|
|
3326
|
-
${
|
|
4005
|
+
${chalk8.bold.cyan("Rush CLI")} ${chalk8.dim(`v${VERSION}`)}
|
|
4006
|
+
${chalk8.dim("The command-line interface for the Rush AI platform")}
|
|
3327
4007
|
`;
|
|
3328
4008
|
function showWelcomeGuide() {
|
|
3329
4009
|
const lines = [
|
|
3330
4010
|
"",
|
|
3331
|
-
` ${
|
|
4011
|
+
` ${chalk8.bold.cyan("Rush CLI")} ${chalk8.dim(`v${VERSION}`)}`,
|
|
3332
4012
|
"",
|
|
3333
|
-
|
|
3334
|
-
` ${
|
|
3335
|
-
` ${
|
|
3336
|
-
` ${
|
|
4013
|
+
chalk8.dim(" Quick start:"),
|
|
4014
|
+
` ${chalk8.cyan("rush-ai auth login")} Log in to Rush platform`,
|
|
4015
|
+
` ${chalk8.cyan("rush-ai agent list")} Browse available agents`,
|
|
4016
|
+
` ${chalk8.cyan("rush-ai task create -a <agent>")} Create a task`,
|
|
3337
4017
|
""
|
|
3338
4018
|
];
|
|
3339
4019
|
try {
|
|
3340
4020
|
const method = getAuthMethod();
|
|
3341
4021
|
if (method) {
|
|
3342
|
-
lines.push(
|
|
4022
|
+
lines.push(chalk8.dim(" Status:"), ` Logged in via ${method}`, "");
|
|
3343
4023
|
}
|
|
3344
4024
|
} catch {
|
|
3345
4025
|
}
|
|
3346
|
-
lines.push(
|
|
4026
|
+
lines.push(chalk8.dim(" Run rush-ai --help for all commands."), "");
|
|
3347
4027
|
console.error(lines.join("\n"));
|
|
3348
4028
|
}
|
|
3349
4029
|
function createProgram() {
|