@interchained/portal-cli 0.1.1
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/commands/adapter.d.ts +5 -0
- package/dist/commands/adapter.d.ts.map +1 -0
- package/dist/commands/adapter.js +71 -0
- package/dist/commands/adapter.js.map +1 -0
- package/dist/commands/agent-cmd.d.ts +7 -0
- package/dist/commands/agent-cmd.d.ts.map +1 -0
- package/dist/commands/agent-cmd.js +162 -0
- package/dist/commands/agent-cmd.js.map +1 -0
- package/dist/commands/audit.d.ts +18 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +80 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/build.d.ts +10 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +35 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/contract-cmd.d.ts +7 -0
- package/dist/commands/contract-cmd.d.ts.map +1 -0
- package/dist/commands/contract-cmd.js +222 -0
- package/dist/commands/contract-cmd.js.map +1 -0
- package/dist/commands/deploy.d.ts +13 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +135 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +10 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +37 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +139 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/explain.d.ts +6 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +75 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/generate.d.ts +13 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +222 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/guard.d.ts +12 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +71 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/improve.d.ts +18 -0
- package/dist/commands/improve.d.ts.map +1 -0
- package/dist/commands/improve.js +157 -0
- package/dist/commands/improve.js.map +1 -0
- package/dist/commands/lint.d.ts +5 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +87 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/patch.d.ts +5 -0
- package/dist/commands/patch.d.ts.map +1 -0
- package/dist/commands/patch.js +78 -0
- package/dist/commands/patch.js.map +1 -0
- package/dist/commands/preview.d.ts +4 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +26 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/rollback.d.ts +8 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +88 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/test.d.ts +7 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +57 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/contract.d.ts +14 -0
- package/dist/utils/contract.d.ts.map +1 -0
- package/dist/utils/contract.js +53 -0
- package/dist/utils/contract.js.map +1 -0
- package/dist/utils/print.d.ts +22 -0
- package/dist/utils/print.d.ts.map +1 -0
- package/dist/utils/print.js +47 -0
- package/dist/utils/print.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* portal contract validate — validate all contract files
|
|
3
|
+
* portal contract init — scaffold default contracts for existing apps
|
|
4
|
+
*/
|
|
5
|
+
import { writeFile, access } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
import prompts from "prompts";
|
|
9
|
+
import { validateContract } from "@interchained/portal-contract";
|
|
10
|
+
import { banner, header, success, fail, warn, info, blank } from "../utils/print.js";
|
|
11
|
+
import { loadContract, findContractPath } from "../utils/contract.js";
|
|
12
|
+
async function exists(p) {
|
|
13
|
+
try {
|
|
14
|
+
await access(p);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// ── portal contract validate ──────────────────────────────────────────────────
|
|
22
|
+
export async function contractValidateCommand() {
|
|
23
|
+
banner();
|
|
24
|
+
header("Contract Validation");
|
|
25
|
+
blank();
|
|
26
|
+
const root = process.cwd();
|
|
27
|
+
let failed = false;
|
|
28
|
+
// Find and load contract
|
|
29
|
+
const contractPath = await findContractPath(root);
|
|
30
|
+
if (!contractPath) {
|
|
31
|
+
fail("No app.contract.ts found — run: portal contract init");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
let contract;
|
|
35
|
+
try {
|
|
36
|
+
contract = await loadContract(root);
|
|
37
|
+
validateContract(contract);
|
|
38
|
+
success("Contract schema valid");
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
fail(`Contract schema invalid: ${err.message}`);
|
|
42
|
+
failed = true;
|
|
43
|
+
}
|
|
44
|
+
if (!failed) {
|
|
45
|
+
// Unreplaced template tokens
|
|
46
|
+
const contractStr = JSON.stringify(contract);
|
|
47
|
+
const tokens = contractStr.match(/\{\{[A-Z_]+\}\}/g);
|
|
48
|
+
if (tokens) {
|
|
49
|
+
const unique = [...new Set(tokens)];
|
|
50
|
+
for (const tok of unique) {
|
|
51
|
+
fail(`Unreplaced token: ${tok}`);
|
|
52
|
+
}
|
|
53
|
+
failed = true;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
success("No unreplaced template tokens");
|
|
57
|
+
}
|
|
58
|
+
// Required fields
|
|
59
|
+
if (!contract.name?.trim()) {
|
|
60
|
+
fail("contract.name is empty");
|
|
61
|
+
failed = true;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
success(`name: "${contract.name}"`);
|
|
65
|
+
}
|
|
66
|
+
if (!contract.goals?.length) {
|
|
67
|
+
fail("contract.goals is empty");
|
|
68
|
+
failed = true;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
success(`goals: ${contract.goals.length} defined`);
|
|
72
|
+
}
|
|
73
|
+
// Pages
|
|
74
|
+
const pages = contract.pages ?? [];
|
|
75
|
+
let pageIssues = 0;
|
|
76
|
+
for (const page of pages) {
|
|
77
|
+
if (!page.purpose?.trim()) {
|
|
78
|
+
warn(`Page "${page.route}" has no purpose`);
|
|
79
|
+
pageIssues++;
|
|
80
|
+
}
|
|
81
|
+
if (!page.primaryAction?.trim()) {
|
|
82
|
+
warn(`Page "${page.route}" has no primaryAction`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (pages.length === 0) {
|
|
86
|
+
warn("No pages declared in contract (optional but recommended)");
|
|
87
|
+
}
|
|
88
|
+
else if (pageIssues === 0) {
|
|
89
|
+
success(`pages: ${pages.length} declared, all have purpose`);
|
|
90
|
+
}
|
|
91
|
+
// Data files
|
|
92
|
+
const data = contract.data ?? {};
|
|
93
|
+
let missingData = 0;
|
|
94
|
+
for (const [name, filePath] of Object.entries(data)) {
|
|
95
|
+
const full = join(root, filePath);
|
|
96
|
+
if (!(await exists(full))) {
|
|
97
|
+
warn(`data.${name}: file missing (${filePath})`);
|
|
98
|
+
missingData++;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
success(`data.${name}: found`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
blank();
|
|
106
|
+
if (failed) {
|
|
107
|
+
fail("Contract validation failed — fix the issues above");
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
success("Contract is valid and ready for agent operations");
|
|
112
|
+
blank();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ── portal contract init ──────────────────────────────────────────────────────
|
|
116
|
+
const DEFAULT_CONTRACT = (name, description) => `import { defineApp } from "@interchained/portal-contract";
|
|
117
|
+
|
|
118
|
+
export default defineApp({
|
|
119
|
+
name: "${name}",
|
|
120
|
+
version: "1.1.0",
|
|
121
|
+
|
|
122
|
+
description: "${description}",
|
|
123
|
+
|
|
124
|
+
goals: [
|
|
125
|
+
"Describe the first thing this app is trying to accomplish",
|
|
126
|
+
"And the second",
|
|
127
|
+
],
|
|
128
|
+
|
|
129
|
+
brand: {
|
|
130
|
+
voice: "clear, direct, professional",
|
|
131
|
+
colors: ["#0f172a", "#6366f1", "#f8fafc"],
|
|
132
|
+
forbiddenPhrases: ["world-class", "best-in-class", "game-changer"],
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
policies: {
|
|
136
|
+
publishing: "human_review",
|
|
137
|
+
accessibility: "basic",
|
|
138
|
+
forbiddenClaims: [],
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
qualityGates: {
|
|
142
|
+
requireMetaTitle: true,
|
|
143
|
+
requireMetaDescription: true,
|
|
144
|
+
requireH1: true,
|
|
145
|
+
requirePrimaryCTA: true,
|
|
146
|
+
forbidPlaceholderCopy: true,
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
pages: [],
|
|
150
|
+
});
|
|
151
|
+
`;
|
|
152
|
+
const DEFAULT_AGENT_CONFIG = `import { defineAgent } from "@interchained/portal-contract";
|
|
153
|
+
|
|
154
|
+
// Add your agents here — run with: portal agent run <name>
|
|
155
|
+
|
|
156
|
+
export const seoAgent = defineAgent({
|
|
157
|
+
name: "seo-reviewer",
|
|
158
|
+
model: "fast",
|
|
159
|
+
sentinel: "smart",
|
|
160
|
+
task: "Review page for SEO gaps and suggest improvements",
|
|
161
|
+
canPatch: true,
|
|
162
|
+
requiresApproval: true,
|
|
163
|
+
});
|
|
164
|
+
`;
|
|
165
|
+
export async function contractInitCommand() {
|
|
166
|
+
banner();
|
|
167
|
+
const root = process.cwd();
|
|
168
|
+
// Check existing
|
|
169
|
+
const existing = await findContractPath(root);
|
|
170
|
+
if (existing) {
|
|
171
|
+
info(`Contract already exists: ${existing}`);
|
|
172
|
+
const { overwrite } = await prompts({
|
|
173
|
+
type: "confirm",
|
|
174
|
+
name: "overwrite",
|
|
175
|
+
message: "Overwrite it?",
|
|
176
|
+
initial: false,
|
|
177
|
+
});
|
|
178
|
+
if (!overwrite) {
|
|
179
|
+
blank();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const { appName } = await prompts({
|
|
184
|
+
type: "text",
|
|
185
|
+
name: "appName",
|
|
186
|
+
message: "App name:",
|
|
187
|
+
initial: root.split("/").pop() ?? "my-portal",
|
|
188
|
+
});
|
|
189
|
+
const { description } = await prompts({
|
|
190
|
+
type: "text",
|
|
191
|
+
name: "description",
|
|
192
|
+
message: "One-line description:",
|
|
193
|
+
initial: "A Portal web application",
|
|
194
|
+
});
|
|
195
|
+
if (!appName) {
|
|
196
|
+
blank();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// Write app.contract.ts
|
|
200
|
+
await writeFile(join(root, "app.contract.ts"), DEFAULT_CONTRACT(appName, description || "A Portal web application"), "utf-8");
|
|
201
|
+
success("Created: app.contract.ts");
|
|
202
|
+
// Write agent.config.ts if it doesn't exist
|
|
203
|
+
const agentConfigPath = join(root, "agent.config.ts");
|
|
204
|
+
if (!(await exists(agentConfigPath))) {
|
|
205
|
+
await writeFile(agentConfigPath, DEFAULT_AGENT_CONFIG, "utf-8");
|
|
206
|
+
success("Created: agent.config.ts");
|
|
207
|
+
}
|
|
208
|
+
// Create agents/ dir
|
|
209
|
+
const agentsDir = join(root, "agents");
|
|
210
|
+
if (!(await exists(agentsDir))) {
|
|
211
|
+
const { mkdir } = await import("node:fs/promises");
|
|
212
|
+
await mkdir(agentsDir, { recursive: true });
|
|
213
|
+
success("Created: agents/");
|
|
214
|
+
}
|
|
215
|
+
blank();
|
|
216
|
+
info("Next steps:");
|
|
217
|
+
console.log(pc.dim(" 1. Edit app.contract.ts — define your goals, brand, and policies"));
|
|
218
|
+
console.log(pc.dim(" 2. portal contract validate — verify it's correct"));
|
|
219
|
+
console.log(pc.dim(" 3. portal audit — run your first audit"));
|
|
220
|
+
blank();
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=contract-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-cmd.js","sourceRoot":"","sources":["../../src/commands/contract-cmd.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAY,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,KAAK,UAAU,MAAM,CAAC,CAAS;IAC7B,IAAI,CAAC;QAAC,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AAC/D,CAAC;AAED,iFAAiF;AAEjF,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,MAAM,EAAE,CAAC;IACT,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC9B,KAAK,EAAE,CAAC;IAER,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,yBAAyB;IACzB,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,IAAI,CAAC,sDAAsD,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,QAA6C,CAAC;IAClD,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QACpC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC3B,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,4BAA6B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,6BAA6B;QAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,QAAS,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACrD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;YACpC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;gBACzB,IAAI,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;YACnC,CAAC;YACD,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,+BAA+B,CAAC,CAAC;QAC3C,CAAC;QAED,kBAAkB;QAClB,IAAI,CAAC,QAAS,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,wBAAwB,CAAC,CAAC;YAAC,MAAM,GAAG,IAAI,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,UAAU,QAAS,CAAC,IAAI,GAAG,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,CAAC,QAAS,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;YAC7B,IAAI,CAAC,yBAAyB,CAAC,CAAC;YAAC,MAAM,GAAG,IAAI,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,UAAU,QAAS,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC;QACtD,CAAC;QAED,QAAQ;QACR,MAAM,KAAK,GAAG,QAAS,CAAC,KAAK,IAAI,EAAE,CAAC;QACpC,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;gBAC1B,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,kBAAkB,CAAC,CAAC;gBAC5C,UAAU,EAAE,CAAC;YACf,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC;gBAChC,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,wBAAwB,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,0DAA0D,CAAC,CAAC;QACnE,CAAC;aAAM,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,UAAU,KAAK,CAAC,MAAM,6BAA6B,CAAC,CAAC;QAC/D,CAAC;QAED,aAAa;QACb,MAAM,IAAI,GAAG,QAAS,CAAC,IAAI,IAAI,EAAE,CAAC;QAClC,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,QAAQ,IAAI,mBAAmB,QAAQ,GAAG,CAAC,CAAC;gBACjD,WAAW,EAAE,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,EAAE,CAAC;IACR,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC,mDAAmD,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,kDAAkD,CAAC,CAAC;QAC5D,KAAK,EAAE,CAAC;IACV,CAAC;AACH,CAAC;AAED,iFAAiF;AAEjF,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAE,WAAmB,EAAE,EAAE,CAAC;;;WAGrD,IAAI;;;kBAGG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B5B,CAAC;AAEF,MAAM,oBAAoB,GAAG;;;;;;;;;;;;CAY5B,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,MAAM,EAAE,CAAC;IAET,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE3B,iBAAiB;IACjB,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;QAC7C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,OAAO,CAAC;YAClC,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,eAAe;YACxB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YAAC,KAAK,EAAE,CAAC;YAAC,OAAO;QAAC,CAAC;IACtC,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC;QAChC,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,WAAW;QACpB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,WAAW;KAC9C,CAAC,CAAC;IAEH,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,OAAO,CAAC;QACpC,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,uBAAuB;QAChC,OAAO,EAAE,0BAA0B;KACpC,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,EAAE,CAAC;QAAC,KAAK,EAAE,CAAC;QAAC,OAAO;IAAC,CAAC;IAElC,wBAAwB;IACxB,MAAM,SAAS,CACb,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAC7B,gBAAgB,CAAC,OAAO,EAAE,WAAW,IAAI,0BAA0B,CAAC,EACpE,OAAO,CACR,CAAC;IACF,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAEpC,4CAA4C;IAC5C,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IACtD,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;QACrC,MAAM,SAAS,CAAC,eAAe,EAAE,oBAAoB,EAAE,OAAO,CAAC,CAAC;QAChE,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACtC,CAAC;IAED,qBAAqB;IACrB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QAC/B,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACnD,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,EAAE,CAAC;IACR,IAAI,CAAC,aAAa,CAAC,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC,CAAC;IAC1F,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC,CAAC;IAChE,KAAK,EAAE,CAAC;AACV,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* portal deploy — build and prepare deployment artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Adapters: static | node | docker
|
|
5
|
+
*/
|
|
6
|
+
export type DeployAdapter = "static" | "node" | "docker";
|
|
7
|
+
export interface DeployOptions {
|
|
8
|
+
adapter?: DeployAdapter;
|
|
9
|
+
outDir?: string;
|
|
10
|
+
tag?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function deployCommand(opts?: DeployOptions): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEzD,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA0CD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiF3E"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* portal deploy — build and prepare deployment artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Adapters: static | node | docker
|
|
5
|
+
*/
|
|
6
|
+
import { writeFile, access } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import pc from "picocolors";
|
|
9
|
+
import { execa } from "execa";
|
|
10
|
+
import prompts from "prompts";
|
|
11
|
+
import { banner, header, success, fail, info, step, blank } from "../utils/print.js";
|
|
12
|
+
const DOCKERFILE = `FROM node:20-alpine AS builder
|
|
13
|
+
WORKDIR /app
|
|
14
|
+
COPY package*.json ./
|
|
15
|
+
RUN npm ci
|
|
16
|
+
COPY . .
|
|
17
|
+
RUN npx vite build
|
|
18
|
+
|
|
19
|
+
FROM node:20-alpine
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
COPY --from=builder /app/dist ./dist
|
|
22
|
+
COPY --from=builder /app/package*.json ./
|
|
23
|
+
RUN npm ci --production
|
|
24
|
+
EXPOSE 3000
|
|
25
|
+
CMD ["node", "dist/server.js"]
|
|
26
|
+
`;
|
|
27
|
+
const SERVER_ENTRY = `import express from "express";
|
|
28
|
+
import { createServer } from "node:http";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
import { dirname, join } from "node:path";
|
|
31
|
+
import { existsSync } from "node:fs";
|
|
32
|
+
|
|
33
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const app = express();
|
|
35
|
+
const PORT = process.env.PORT ?? 3000;
|
|
36
|
+
|
|
37
|
+
app.use(express.static(join(__dirname, ".")));
|
|
38
|
+
app.get("*", (_req, res) => {
|
|
39
|
+
res.sendFile(join(__dirname, "index.html"));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
createServer(app).listen(PORT, () => {
|
|
43
|
+
console.log(\`Portal app running on port \${PORT}\`);
|
|
44
|
+
});
|
|
45
|
+
`;
|
|
46
|
+
async function exists(p) {
|
|
47
|
+
try {
|
|
48
|
+
await access(p);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function deployCommand(opts = {}) {
|
|
56
|
+
banner();
|
|
57
|
+
const root = process.cwd();
|
|
58
|
+
let adapter = opts.adapter;
|
|
59
|
+
if (!adapter) {
|
|
60
|
+
const { chosen } = await prompts({
|
|
61
|
+
type: "select",
|
|
62
|
+
name: "chosen",
|
|
63
|
+
message: "Choose deployment adapter:",
|
|
64
|
+
choices: [
|
|
65
|
+
{ title: `${pc.bold("static")} ${pc.dim("— CDN / Netlify / Vercel / S3")}`, value: "static" },
|
|
66
|
+
{ title: `${pc.bold("node")} ${pc.dim("— Node.js server (Express)")}`, value: "node" },
|
|
67
|
+
{ title: `${pc.bold("docker")} ${pc.dim("— Docker container (self-hosted)")}`, value: "docker" },
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
adapter = chosen;
|
|
71
|
+
}
|
|
72
|
+
if (!adapter) {
|
|
73
|
+
blank();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
header(`Deploying: ${pc.bold(adapter)}`);
|
|
77
|
+
blank();
|
|
78
|
+
// Always build first
|
|
79
|
+
step("Building production bundle…");
|
|
80
|
+
try {
|
|
81
|
+
await execa("npx", ["vite", "build", "--outDir", opts.outDir ?? "dist"], {
|
|
82
|
+
cwd: root,
|
|
83
|
+
stdio: "inherit",
|
|
84
|
+
env: { ...process.env, FORCE_COLOR: "1" },
|
|
85
|
+
});
|
|
86
|
+
success("Build complete");
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
fail("Build failed — fix errors before deploying");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
blank();
|
|
93
|
+
if (adapter === "static") {
|
|
94
|
+
success("Static build ready in dist/");
|
|
95
|
+
info("Deploy dist/ to any static host: Netlify, Vercel, S3, Cloudflare Pages, or rsync.");
|
|
96
|
+
}
|
|
97
|
+
else if (adapter === "node") {
|
|
98
|
+
// Write server entry if missing
|
|
99
|
+
const serverPath = join(root, "dist", "server.js");
|
|
100
|
+
if (!(await exists(serverPath))) {
|
|
101
|
+
step("Writing Node.js server entry (dist/server.js)…");
|
|
102
|
+
await writeFile(serverPath, SERVER_ENTRY, "utf-8");
|
|
103
|
+
success("dist/server.js written");
|
|
104
|
+
}
|
|
105
|
+
info("Run: node dist/server.js");
|
|
106
|
+
info("Set PORT env var to choose the port (default 3000).");
|
|
107
|
+
}
|
|
108
|
+
else if (adapter === "docker") {
|
|
109
|
+
const dockerfilePath = join(root, "Dockerfile");
|
|
110
|
+
if (!(await exists(dockerfilePath))) {
|
|
111
|
+
step("Writing Dockerfile…");
|
|
112
|
+
await writeFile(dockerfilePath, DOCKERFILE, "utf-8");
|
|
113
|
+
success("Dockerfile written");
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
info("Dockerfile already exists — using existing one");
|
|
117
|
+
}
|
|
118
|
+
const tag = opts.tag ?? "portal-app:latest";
|
|
119
|
+
step(`Building Docker image: ${tag}…`);
|
|
120
|
+
try {
|
|
121
|
+
await execa("docker", ["build", "-t", tag, "."], {
|
|
122
|
+
cwd: root,
|
|
123
|
+
stdio: "inherit",
|
|
124
|
+
});
|
|
125
|
+
success(`Docker image built: ${tag}`);
|
|
126
|
+
info(`Run: docker run -p 3000:3000 ${tag}`);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
console.log(pc.yellow("⚠ Docker build failed — make sure Docker is running"));
|
|
130
|
+
info("Dockerfile written to project root — build manually when ready.");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
blank();
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* portal dev — starts the Vite dev server with Portal plugin.
|
|
3
|
+
*/
|
|
4
|
+
export interface DevOptions {
|
|
5
|
+
port?: number;
|
|
6
|
+
host?: boolean;
|
|
7
|
+
open?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function devCommand(opts?: DevOptions): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BrE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* portal dev — starts the Vite dev server with Portal plugin.
|
|
3
|
+
*/
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { execa } from "execa";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
import { banner, step } from "../utils/print.js";
|
|
8
|
+
export async function devCommand(opts = {}) {
|
|
9
|
+
banner();
|
|
10
|
+
const root = process.cwd();
|
|
11
|
+
const viteConfig = join(root, "vite.config.ts");
|
|
12
|
+
step(`Starting dev server${opts.port ? ` on port ${opts.port}` : ""}…`);
|
|
13
|
+
console.log(pc.dim(` root: ${root}`));
|
|
14
|
+
console.log();
|
|
15
|
+
const args = ["vite", "--config", viteConfig];
|
|
16
|
+
if (opts.port)
|
|
17
|
+
args.push("--port", String(opts.port));
|
|
18
|
+
if (opts.host)
|
|
19
|
+
args.push("--host");
|
|
20
|
+
if (opts.open)
|
|
21
|
+
args.push("--open");
|
|
22
|
+
try {
|
|
23
|
+
await execa("npx", args, {
|
|
24
|
+
stdio: "inherit",
|
|
25
|
+
cwd: root,
|
|
26
|
+
env: { ...process.env, FORCE_COLOR: "1" },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
// Vite exits non-zero on SIGINT — that's fine
|
|
31
|
+
const code = err.exitCode;
|
|
32
|
+
if (code !== 0 && code !== null && code !== undefined) {
|
|
33
|
+
process.exit(code);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAQ,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAQvD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAmB,EAAE;IACpD,MAAM,EAAE,CAAC;IAET,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAEhD,IAAI,CAAC,sBAAsB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IAC9C,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE;YACvB,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,IAAI;YACT,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE;SAC1C,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,8CAA8C;QAC9C,MAAM,IAAI,GAAI,GAA6B,CAAC,QAAQ,CAAC;QACrD,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACtD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAgInD"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* portal doctor — environment + project health check.
|
|
3
|
+
* Runs before you let agents near your code.
|
|
4
|
+
*/
|
|
5
|
+
import { access, readdir } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
import { banner, header, success, warn, fail, blank } from "../utils/print.js";
|
|
9
|
+
import { loadContract, findContractPath } from "../utils/contract.js";
|
|
10
|
+
async function exists(p) {
|
|
11
|
+
try {
|
|
12
|
+
await access(p);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function doctorCommand() {
|
|
20
|
+
banner();
|
|
21
|
+
header("Portal Doctor");
|
|
22
|
+
const root = process.cwd();
|
|
23
|
+
const checks = [];
|
|
24
|
+
// Node version
|
|
25
|
+
const nodeVer = process.version;
|
|
26
|
+
const nodeMaj = parseInt(nodeVer.slice(1), 10);
|
|
27
|
+
checks.push({
|
|
28
|
+
label: `Node.js ${nodeVer}`,
|
|
29
|
+
status: nodeMaj >= 18 ? "ok" : "fail",
|
|
30
|
+
detail: nodeMaj < 18 ? "Portal requires Node.js 18 or newer" : undefined,
|
|
31
|
+
});
|
|
32
|
+
// vite.config.ts / portal.config.ts
|
|
33
|
+
const hasViteConfig = (await exists(join(root, "vite.config.ts"))) ||
|
|
34
|
+
(await exists(join(root, "vite.config.js"))) ||
|
|
35
|
+
(await exists(join(root, "portal.config.ts")));
|
|
36
|
+
checks.push({
|
|
37
|
+
label: "vite.config.ts",
|
|
38
|
+
status: hasViteConfig ? "ok" : "warn",
|
|
39
|
+
detail: !hasViteConfig ? "No Vite config found — run: npm create portal-app" : undefined,
|
|
40
|
+
});
|
|
41
|
+
// app.contract.ts
|
|
42
|
+
const contractPath = await findContractPath(root);
|
|
43
|
+
checks.push({
|
|
44
|
+
label: "app.contract.ts",
|
|
45
|
+
status: contractPath ? "ok" : "warn",
|
|
46
|
+
detail: !contractPath
|
|
47
|
+
? "No contract found — run: portal contract init"
|
|
48
|
+
: undefined,
|
|
49
|
+
});
|
|
50
|
+
// routes/ directory
|
|
51
|
+
const routesDir = join(root, "routes");
|
|
52
|
+
const hasRoutes = await exists(routesDir);
|
|
53
|
+
let routeCount = 0;
|
|
54
|
+
if (hasRoutes) {
|
|
55
|
+
try {
|
|
56
|
+
const entries = await readdir(routesDir, { recursive: true });
|
|
57
|
+
routeCount = entries.filter((e) => e.endsWith(".page.tsx") || e.endsWith(".page.jsx")).length;
|
|
58
|
+
}
|
|
59
|
+
catch { /* */ }
|
|
60
|
+
}
|
|
61
|
+
checks.push({
|
|
62
|
+
label: `routes/ (${routeCount} page${routeCount !== 1 ? "s" : ""})`,
|
|
63
|
+
status: hasRoutes && routeCount > 0 ? "ok" : "warn",
|
|
64
|
+
detail: !hasRoutes
|
|
65
|
+
? "No routes/ directory found"
|
|
66
|
+
: routeCount === 0
|
|
67
|
+
? "routes/ exists but has no *.page.tsx files"
|
|
68
|
+
: undefined,
|
|
69
|
+
});
|
|
70
|
+
// package.json
|
|
71
|
+
const hasPkg = await exists(join(root, "package.json"));
|
|
72
|
+
checks.push({
|
|
73
|
+
label: "package.json",
|
|
74
|
+
status: hasPkg ? "ok" : "fail",
|
|
75
|
+
detail: !hasPkg ? "No package.json — are you in the right directory?" : undefined,
|
|
76
|
+
});
|
|
77
|
+
// AI API key
|
|
78
|
+
const hasApiKey = !!process.env["AIASSIST_API_KEY"] || !!process.env["VITE_AIAS_API_KEY"];
|
|
79
|
+
checks.push({
|
|
80
|
+
label: "AIASSIST_API_KEY",
|
|
81
|
+
status: hasApiKey ? "ok" : "warn",
|
|
82
|
+
detail: !hasApiKey
|
|
83
|
+
? "Not set — portal audit --ai, portal improve, and portal generate require this"
|
|
84
|
+
: undefined,
|
|
85
|
+
});
|
|
86
|
+
// Data files referenced in contract
|
|
87
|
+
if (contractPath) {
|
|
88
|
+
const contract = await loadContract(root);
|
|
89
|
+
for (const [name, filePath] of Object.entries(contract.data ?? {})) {
|
|
90
|
+
const fullPath = join(root, filePath);
|
|
91
|
+
const fileExists = await exists(fullPath);
|
|
92
|
+
checks.push({
|
|
93
|
+
label: `data.${name} (${filePath})`,
|
|
94
|
+
status: fileExists ? "ok" : "warn",
|
|
95
|
+
detail: !fileExists ? `Data file missing: ${filePath}` : undefined,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// Check for unreplaced tokens in contract name
|
|
99
|
+
if (contract.name.includes("{{")) {
|
|
100
|
+
checks.push({
|
|
101
|
+
label: "contract.name",
|
|
102
|
+
status: "fail",
|
|
103
|
+
detail: `Contract name contains unreplaced token: "${contract.name}"`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Print results
|
|
108
|
+
blank();
|
|
109
|
+
for (const check of checks) {
|
|
110
|
+
if (check.status === "ok") {
|
|
111
|
+
success(check.label);
|
|
112
|
+
}
|
|
113
|
+
else if (check.status === "warn") {
|
|
114
|
+
warn(check.label);
|
|
115
|
+
if (check.detail)
|
|
116
|
+
console.log(pc.dim(` ${check.detail}`));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
fail(check.label);
|
|
120
|
+
if (check.detail)
|
|
121
|
+
console.log(pc.red(` ${check.detail}`));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
blank();
|
|
125
|
+
const failCount = checks.filter((c) => c.status === "fail").length;
|
|
126
|
+
const warnCount = checks.filter((c) => c.status === "warn").length;
|
|
127
|
+
if (failCount > 0) {
|
|
128
|
+
fail(`${failCount} critical issue${failCount !== 1 ? "s" : ""} found — fix before proceeding`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
else if (warnCount > 0) {
|
|
132
|
+
warn(`${warnCount} warning${warnCount !== 1 ? "s" : ""} — not blocking, but worth fixing`);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
success("All checks passed — project looks healthy");
|
|
136
|
+
}
|
|
137
|
+
blank();
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../src/commands/explain.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsElE"}
|