@pulse-editor/cli 0.1.2 → 0.1.3
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/app.js
CHANGED
|
@@ -14,6 +14,7 @@ import Start from './components/commands/start.js';
|
|
|
14
14
|
import Clean from './components/commands/clean.js';
|
|
15
15
|
import Upgrade from './components/commands/upgrade.js';
|
|
16
16
|
import Skill from './components/commands/skill.js';
|
|
17
|
+
import Code from './components/commands/code.js';
|
|
17
18
|
export default function App({ cli }) {
|
|
18
19
|
const [command, setCommand] = useState(undefined);
|
|
19
20
|
if (cli.flags.stage) {
|
|
@@ -26,5 +27,5 @@ export default function App({ cli }) {
|
|
|
26
27
|
const cmd = cli.input[0] ?? 'help';
|
|
27
28
|
setCommand(cmd);
|
|
28
29
|
}, [cli.input]);
|
|
29
|
-
return (_jsxs(_Fragment, { children: [cli.flags.stage && (_jsx(Text, { color: 'yellow', children: "\u26A0\uFE0F You are in development mode." })), command === 'help' ? (_jsx(Help, { cli: cli })) : command === 'chat' ? (_jsx(Chat, { cli: cli })) : command === 'login' ? (_jsx(Login, { cli: cli })) : command === 'logout' ? (_jsx(Logout, { cli: cli })) : command === 'publish' ? (_jsx(Publish, { cli: cli })) : command === 'create' ? (_jsx(Create, { cli: cli })) : command === 'dev' ? (_jsx(Dev, { cli: cli })) : command === 'build' ? (_jsx(Build, { cli: cli })) : command === 'preview' ? (_jsx(Preview, { cli: cli })) : command === 'start' ? (_jsx(Start, { cli: cli })) : command === 'clean' ? (_jsx(Clean, { cli: cli })) : command === 'upgrade' ? (_jsx(Upgrade, { cli: cli })) : command === 'skill' ? (_jsx(Skill, { cli: cli })) : (command !== undefined && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: 'redBright', children: ["Invalid command: ", command] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: 'blueBright', children: "pulse help" }), " to see the list of available commands."] })] })))] }));
|
|
30
|
+
return (_jsxs(_Fragment, { children: [cli.flags.stage && (_jsx(Text, { color: 'yellow', children: "\u26A0\uFE0F You are in development mode." })), command === 'help' ? (_jsx(Help, { cli: cli })) : command === 'chat' ? (_jsx(Chat, { cli: cli })) : command === 'login' ? (_jsx(Login, { cli: cli })) : command === 'logout' ? (_jsx(Logout, { cli: cli })) : command === 'publish' ? (_jsx(Publish, { cli: cli })) : command === 'create' ? (_jsx(Create, { cli: cli })) : command === 'dev' ? (_jsx(Dev, { cli: cli })) : command === 'build' ? (_jsx(Build, { cli: cli })) : command === 'preview' ? (_jsx(Preview, { cli: cli })) : command === 'start' ? (_jsx(Start, { cli: cli })) : command === 'clean' ? (_jsx(Clean, { cli: cli })) : command === 'upgrade' ? (_jsx(Upgrade, { cli: cli })) : command === 'skill' ? (_jsx(Skill, { cli: cli })) : command === 'code' ? (_jsx(Code, { cli: cli })) : (command !== undefined && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: 'redBright', children: ["Invalid command: ", command] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: 'blueBright', children: "pulse help" }), " to see the list of available commands."] })] })))] }));
|
|
30
31
|
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import Spinner from "ink-spinner";
|
|
5
|
+
import TextInput from "ink-text-input";
|
|
6
|
+
import JSZip from "jszip";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { useEffect, useMemo, useState } from "react";
|
|
9
|
+
import { getBackendUrl } from "../../lib/backend-url.js";
|
|
10
|
+
import { checkToken, getToken } from "../../lib/token.js";
|
|
11
|
+
function readContinueData(cwd) {
|
|
12
|
+
const pkgPath = path.resolve(cwd, "package.json");
|
|
13
|
+
if (!fs.existsSync(pkgPath)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
18
|
+
const name = typeof pkg["name"] === "string" ? pkg["name"] : undefined;
|
|
19
|
+
const version = typeof pkg["version"] === "string" ? pkg["version"] : undefined;
|
|
20
|
+
const appId = typeof pkg["appId"] === "string" ? pkg["appId"] : name;
|
|
21
|
+
if (!appId || !version) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return { appId, version, name: name ?? appId };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function makeAppName(prompt) {
|
|
31
|
+
const normalized = prompt
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replaceAll(/[^a-z0-9\s-]/g, "")
|
|
34
|
+
.trim()
|
|
35
|
+
.split(/\s+/)
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.slice(0, 5)
|
|
38
|
+
.join("_");
|
|
39
|
+
const suffix = Math.random().toString(36).slice(2, 8);
|
|
40
|
+
return `${normalized || "pulse_app"}_${suffix}`;
|
|
41
|
+
}
|
|
42
|
+
function parseArtifact(result) {
|
|
43
|
+
if (!result) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(result);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export default function Code({ cli }) {
|
|
54
|
+
// ── Continue mode ────────────────────────────────────────────────────────
|
|
55
|
+
const continueData = useMemo(() => {
|
|
56
|
+
if (!cli.flags.continue)
|
|
57
|
+
return undefined;
|
|
58
|
+
return readContinueData(process.cwd());
|
|
59
|
+
}, [cli.flags.continue]);
|
|
60
|
+
// ── Resolved inputs ──────────────────────────────────────────────────────
|
|
61
|
+
const [appName, setAppName] = useState(() => {
|
|
62
|
+
if (cli.flags.name)
|
|
63
|
+
return cli.flags.name;
|
|
64
|
+
if (cli.flags.continue) {
|
|
65
|
+
const data = readContinueData(process.cwd());
|
|
66
|
+
return data?.name ?? undefined;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
});
|
|
70
|
+
const [appNameInput, setAppNameInput] = useState("");
|
|
71
|
+
const [prompt, setPrompt] = useState(undefined);
|
|
72
|
+
const [promptInput, setPromptInput] = useState("");
|
|
73
|
+
// ── Auth ─────────────────────────────────────────────────────────────────
|
|
74
|
+
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
|
|
75
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
76
|
+
// ── Generation state ─────────────────────────────────────────────────────
|
|
77
|
+
const [isGenerating, setIsGenerating] = useState(false);
|
|
78
|
+
const [isGenerated, setIsGenerated] = useState(false);
|
|
79
|
+
const [error, setError] = useState(undefined);
|
|
80
|
+
const [statusLines, setStatusLines] = useState([]);
|
|
81
|
+
// Per-message live streaming buffers
|
|
82
|
+
const [liveBuffers, setLiveBuffers] = useState(new Map());
|
|
83
|
+
const [toolCallErrors, setToolCallErrors] = useState([]);
|
|
84
|
+
const [artifact, setArtifact] = useState(undefined);
|
|
85
|
+
const [isDownloading, setIsDownloading] = useState(false);
|
|
86
|
+
const [downloadedPath, setDownloadedPath] = useState(undefined);
|
|
87
|
+
const [downloadError, setDownloadError] = useState(undefined);
|
|
88
|
+
const token = useMemo(() => getToken(cli.flags.stage), [cli.flags.stage]);
|
|
89
|
+
// Pre-fill prompt from inline CLI args, e.g. `pulse code "my prompt"`
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const initialPrompt = cli.input.slice(1).join(" ").trim();
|
|
92
|
+
if (initialPrompt) {
|
|
93
|
+
setPrompt(initialPrompt);
|
|
94
|
+
}
|
|
95
|
+
}, [cli.input]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
async function runAuthCheck() {
|
|
98
|
+
if (!token) {
|
|
99
|
+
setIsAuthenticated(false);
|
|
100
|
+
setIsCheckingAuth(false);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const isValid = await checkToken(token, cli.flags.stage);
|
|
104
|
+
setIsAuthenticated(isValid);
|
|
105
|
+
setIsCheckingAuth(false);
|
|
106
|
+
}
|
|
107
|
+
runAuthCheck();
|
|
108
|
+
}, [token, cli.flags.stage]);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
async function runCodeGeneration(userPrompt, resolvedAppName) {
|
|
111
|
+
if (!token) {
|
|
112
|
+
setError("Missing access token. Please run pulse login first.");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
setError(undefined);
|
|
116
|
+
setStatusLines([]);
|
|
117
|
+
setLiveBuffers(new Map());
|
|
118
|
+
setToolCallErrors([]);
|
|
119
|
+
setArtifact(undefined);
|
|
120
|
+
setIsDownloading(false);
|
|
121
|
+
setDownloadedPath(undefined);
|
|
122
|
+
setDownloadError(undefined);
|
|
123
|
+
setIsGenerating(true);
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
let didTimeout = false;
|
|
126
|
+
const timeoutId = setTimeout(() => {
|
|
127
|
+
didTimeout = true;
|
|
128
|
+
controller.abort("Generation timeout after 5 minutes");
|
|
129
|
+
}, 5 * 60 * 1000);
|
|
130
|
+
const collectedMessages = new Map();
|
|
131
|
+
// Per-message live streaming buffers
|
|
132
|
+
const localBuffers = new Map();
|
|
133
|
+
let _artifact = undefined;
|
|
134
|
+
try {
|
|
135
|
+
const response = await fetch(`${getBackendUrl(cli.flags.stage)}/api/server-function/vibe_dev_flow/latest/generate-code/v2/generate`, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
Authorization: `Bearer ${token}`,
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
},
|
|
141
|
+
body: JSON.stringify({
|
|
142
|
+
prompt: userPrompt,
|
|
143
|
+
...(continueData
|
|
144
|
+
? {
|
|
145
|
+
appId: continueData.appId,
|
|
146
|
+
version: continueData.version,
|
|
147
|
+
}
|
|
148
|
+
: {
|
|
149
|
+
appName: resolvedAppName,
|
|
150
|
+
}),
|
|
151
|
+
}),
|
|
152
|
+
signal: controller.signal,
|
|
153
|
+
});
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
throw new Error(`Code generation failed: ${response.status} ${response.statusText} - ${await response.text()}`);
|
|
156
|
+
}
|
|
157
|
+
if (!response.body) {
|
|
158
|
+
throw new Error("No stream returned from server.");
|
|
159
|
+
}
|
|
160
|
+
const reader = response.body.getReader();
|
|
161
|
+
const decoder = new TextDecoder();
|
|
162
|
+
let buffer = "";
|
|
163
|
+
while (true) {
|
|
164
|
+
const { value, done } = await reader.read();
|
|
165
|
+
if (done) {
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
buffer += decoder.decode(value, { stream: true });
|
|
169
|
+
const parts = buffer.split("\n\n");
|
|
170
|
+
buffer = parts.pop() ?? "";
|
|
171
|
+
for (const part of parts) {
|
|
172
|
+
if (!part.startsWith("data:")) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const json = part.replace(/^data:\s*/, "");
|
|
176
|
+
let message;
|
|
177
|
+
try {
|
|
178
|
+
message = JSON.parse(json);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (message.type === "creation") {
|
|
184
|
+
collectedMessages.set(message.messageId, message);
|
|
185
|
+
if (message.data.type === "notification") {
|
|
186
|
+
const text = [message.data.title, message.data.description]
|
|
187
|
+
.filter(Boolean)
|
|
188
|
+
.join(": ");
|
|
189
|
+
if (text.length > 0) {
|
|
190
|
+
setStatusLines((previous) => [...previous, { text }]);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (message.data.type === "toolCall") {
|
|
194
|
+
const label = message.data.title
|
|
195
|
+
? `Tool call: ${message.data.title}`
|
|
196
|
+
: "Tool call executed";
|
|
197
|
+
setStatusLines((previous) => [
|
|
198
|
+
...previous,
|
|
199
|
+
{
|
|
200
|
+
text: label,
|
|
201
|
+
messageId: message.messageId,
|
|
202
|
+
toolTitle: message.data.title,
|
|
203
|
+
},
|
|
204
|
+
]);
|
|
205
|
+
// Start tracking this tool call's buffer
|
|
206
|
+
localBuffers.set(message.messageId, message.data.description
|
|
207
|
+
? `${message.data.description}\n`
|
|
208
|
+
: "");
|
|
209
|
+
setLiveBuffers(new Map(localBuffers));
|
|
210
|
+
}
|
|
211
|
+
if (message.data.type === "artifactOutput") {
|
|
212
|
+
_artifact = parseArtifact(message.data.result);
|
|
213
|
+
setArtifact(_artifact);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// update message
|
|
218
|
+
const existing = collectedMessages.get(message.messageId);
|
|
219
|
+
if (!existing) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const deltaResult = message.delta.result ?? "";
|
|
223
|
+
const deltaError = message.delta.error ?? "";
|
|
224
|
+
existing.data.result = (existing.data.result ?? "") + deltaResult;
|
|
225
|
+
existing.data.error = (existing.data.error ?? "") + deltaError;
|
|
226
|
+
existing.isFinal = message.isFinal;
|
|
227
|
+
// Accumulate delta into this message's own buffer
|
|
228
|
+
if (localBuffers.has(message.messageId) &&
|
|
229
|
+
deltaResult.length > 0) {
|
|
230
|
+
const current = localBuffers.get(message.messageId) ?? "";
|
|
231
|
+
localBuffers.set(message.messageId, current + deltaResult);
|
|
232
|
+
setLiveBuffers(new Map(localBuffers));
|
|
233
|
+
}
|
|
234
|
+
// Surface tool-level errors when the message is finalized
|
|
235
|
+
if (message.isFinal && existing.data.error) {
|
|
236
|
+
const errText = existing.data.error.trim();
|
|
237
|
+
if (errText.length > 0) {
|
|
238
|
+
setToolCallErrors((previous) => [...previous, errText]);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
setIsGenerated(true);
|
|
245
|
+
// Download the source zip
|
|
246
|
+
if (_artifact && _artifact.appId) {
|
|
247
|
+
setIsDownloading(true);
|
|
248
|
+
try {
|
|
249
|
+
// Step 1: exchange the archive endpoint for a SAS URL
|
|
250
|
+
const sasEndpoint = `${getBackendUrl(cli.flags.stage)}/api/app/source?app=${encodeURIComponent(_artifact.appId)}`;
|
|
251
|
+
const sasResponse = await fetch(sasEndpoint, {
|
|
252
|
+
headers: {
|
|
253
|
+
Accept: "application/zip",
|
|
254
|
+
Authorization: `Bearer ${token}`,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
if (!sasResponse.ok) {
|
|
258
|
+
throw new Error(`Failed to get download link for ${_artifact.appId}: ${sasResponse.status} ${sasResponse.statusText}`);
|
|
259
|
+
}
|
|
260
|
+
const { url: sasUrl } = (await sasResponse.json());
|
|
261
|
+
console.log(`SAS URL for ${_artifact.appId}: ${sasUrl}`);
|
|
262
|
+
// Step 2: download the actual zip from the SAS URL
|
|
263
|
+
const downloadResponse = await fetch(sasUrl);
|
|
264
|
+
if (!downloadResponse.ok) {
|
|
265
|
+
throw new Error(`Failed to download archive: ${downloadResponse.status} ${downloadResponse.statusText}`);
|
|
266
|
+
}
|
|
267
|
+
const arrayBuffer = await downloadResponse.arrayBuffer();
|
|
268
|
+
// Extract zip in-memory and write files one by one
|
|
269
|
+
const zip = await JSZip.loadAsync(arrayBuffer);
|
|
270
|
+
const destDir = process.cwd();
|
|
271
|
+
for (const [relativePath, zipEntry] of Object.entries(zip.files)) {
|
|
272
|
+
if (zipEntry.dir)
|
|
273
|
+
continue;
|
|
274
|
+
const filePath = path.join(destDir, relativePath);
|
|
275
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
276
|
+
const content = await zipEntry.async("nodebuffer");
|
|
277
|
+
fs.writeFileSync(filePath, content);
|
|
278
|
+
}
|
|
279
|
+
setDownloadedPath(destDir);
|
|
280
|
+
}
|
|
281
|
+
catch (downloadErr) {
|
|
282
|
+
setDownloadError(downloadErr instanceof Error
|
|
283
|
+
? downloadErr.message
|
|
284
|
+
: "Failed to download source archive.");
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
setIsDownloading(false);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch (generationError) {
|
|
292
|
+
if (controller.signal.aborted && didTimeout) {
|
|
293
|
+
setError("Code generation timed out after 5 minutes.");
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
setError(generationError instanceof Error
|
|
297
|
+
? generationError.message
|
|
298
|
+
: "Failed to generate code.");
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
clearTimeout(timeoutId);
|
|
303
|
+
setIsGenerating(false);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (isAuthenticated && prompt && appName && continueData !== null) {
|
|
307
|
+
runCodeGeneration(prompt, appName);
|
|
308
|
+
}
|
|
309
|
+
}, [prompt, appName, cli.flags.stage, isAuthenticated, token, continueData]);
|
|
310
|
+
function getLiveLines(messageId, toolTitle) {
|
|
311
|
+
const text = liveBuffers.get(messageId);
|
|
312
|
+
if (!text)
|
|
313
|
+
return { lines: [], truncated: false };
|
|
314
|
+
const all = text.split("\n").filter((l) => l.length > 0);
|
|
315
|
+
if (toolTitle === "Agent Message") {
|
|
316
|
+
return { lines: all, truncated: false };
|
|
317
|
+
}
|
|
318
|
+
const truncated = all.length > 8;
|
|
319
|
+
return { lines: all.slice(-8), truncated };
|
|
320
|
+
}
|
|
321
|
+
return (_jsx(_Fragment, { children: isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : !isAuthenticated ? (_jsx(Text, { color: "redBright", children: "\u26D4 You are not authenticated. Run pulse login and try again." })) : cli.flags.continue && continueData === null ? (_jsx(Text, { color: "redBright", children: "\u26D4 Could not read app name and version from package.json in the current directory. Make sure a valid package.json with \"name\", \"version\" (and optionally \"appId\") exists." })) : appName === undefined ? (
|
|
322
|
+
// Step 1 — app name
|
|
323
|
+
_jsxs(Box, { children: [_jsx(Text, { children: "App name: " }), _jsx(TextInput, { value: appNameInput, onChange: setAppNameInput, onSubmit: (value) => {
|
|
324
|
+
const trimmed = value.trim();
|
|
325
|
+
setAppName(trimmed.length > 0 ? trimmed : makeAppName(""));
|
|
326
|
+
} })] })) : prompt === undefined ? (
|
|
327
|
+
// Step 2 — prompt
|
|
328
|
+
_jsxs(Box, { children: [_jsx(Text, { children: "Describe the Pulse app you want to generate: " }), _jsx(TextInput, { value: promptInput, onChange: setPromptInput, onSubmit: (value) => {
|
|
329
|
+
const trimmed = value.trim();
|
|
330
|
+
if (trimmed.length === 0) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
setPrompt(trimmed);
|
|
334
|
+
} })] })) : (
|
|
335
|
+
// Step 3 — generating
|
|
336
|
+
_jsxs(_Fragment, { children: [cli.flags.continue && continueData && (_jsxs(Text, { dimColor: true, children: ["Continuing from v", continueData.version, " (", continueData.appId, ")"] })), _jsxs(Text, { children: ["App name: ", _jsx(Text, { color: "yellow", children: appName })] }), _jsxs(Text, { children: ["Prompt: ", _jsx(Text, { color: "cyan", children: prompt })] }), isGenerating && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Generating app..." })] })), statusLines.map((item, index) => {
|
|
337
|
+
const { lines, truncated } = item.messageId !== undefined
|
|
338
|
+
? getLiveLines(item.messageId, item.toolTitle)
|
|
339
|
+
: { lines: [], truncated: false };
|
|
340
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: index < statusLines.length - 1, children: ["\u2022 ", item.text] }), lines.length > 0 && (_jsxs(Box, { borderStyle: "round", borderColor: "gray", flexDirection: "column", paddingX: 1, children: [truncated && (_jsx(Text, { color: "gray", dimColor: true, children: "<truncated>" })), lines.map((line, lineIndex) => (_jsx(Text, { color: "gray", children: line }, lineIndex)))] }))] }, `${item.text}-${index}`));
|
|
341
|
+
}), toolCallErrors.map((err, index) => (_jsxs(Text, { color: "redBright", children: ["\u274C ", err] }, index))), error && _jsxs(Text, { color: "redBright", children: ["\u274C ", error] }), isGenerated && !error && (_jsx(Text, { color: "greenBright", children: "\u2705 Code generation completed." })), artifact?.publishedAppLink && (_jsxs(Text, { color: "greenBright", children: ["Preview: ", artifact.publishedAppLink] })), artifact?.sourceCodeArchiveLink && (_jsxs(Text, { color: "greenBright", children: ["Source (.zip): ", artifact.sourceCodeArchiveLink] })), isDownloading && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Downloading source archive..." })] })), downloadedPath && (_jsxs(Text, { color: "greenBright", children: ["Source extracted: ", downloadedPath] })), downloadError && (_jsxs(Text, { color: "redBright", children: ["\u274C Download failed: ", downloadError] }))] })) }));
|
|
342
|
+
}
|
package/dist/lib/cli-flags.d.ts
CHANGED
package/dist/lib/cli-flags.js
CHANGED
package/dist/lib/manual.js
CHANGED
|
@@ -9,6 +9,17 @@ const chat = `\
|
|
|
9
9
|
--interactive, -i
|
|
10
10
|
Start an interactive chat session
|
|
11
11
|
|
|
12
|
+
`;
|
|
13
|
+
const code = `\
|
|
14
|
+
code Generate a new Pulse App from a prompt using AI.
|
|
15
|
+
|
|
16
|
+
Flags:
|
|
17
|
+
--name, -n [app-name]
|
|
18
|
+
Optional app name used for generation.
|
|
19
|
+
--continue, -c
|
|
20
|
+
Continue building on an existing app. Reads the app ID
|
|
21
|
+
and version from the package.json in the current directory.
|
|
22
|
+
|
|
12
23
|
`;
|
|
13
24
|
const login = `\
|
|
14
25
|
login Login to the Pulse Editor Platform.
|
|
@@ -101,6 +112,7 @@ const skill = `\
|
|
|
101
112
|
export const commandsManual = {
|
|
102
113
|
help,
|
|
103
114
|
chat,
|
|
115
|
+
code,
|
|
104
116
|
login,
|
|
105
117
|
logout,
|
|
106
118
|
publish,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pulse-editor/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pulse": "dist/cli.js"
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"ink-select-input": "^6.2.0",
|
|
38
38
|
"ink-spinner": "^5.0.0",
|
|
39
39
|
"ink-text-input": "^6.0.0",
|
|
40
|
+
"jszip": "^3.10.1",
|
|
40
41
|
"livereload": "^0.10.3",
|
|
41
42
|
"meow": "^14.1.0",
|
|
42
43
|
"mini-css-extract-plugin": "^2.10.0",
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"@types/connect-livereload": "^0.6.3",
|
|
53
54
|
"@types/cors": "^2.8.19",
|
|
54
55
|
"@types/express": "^5.0.6",
|
|
56
|
+
"@types/jszip": "^3.4.0",
|
|
55
57
|
"@types/livereload": "^0.9.5",
|
|
56
58
|
"@types/react": "^19.2.14",
|
|
57
59
|
"@types/react-dom": "^19.2.3",
|