@pulse-editor/cli 0.1.1-beta.9 → 0.1.2

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.
Files changed (57) hide show
  1. package/dist/app.js +3 -1
  2. package/dist/components/commands/build.js +18 -33
  3. package/dist/components/commands/create.js +38 -12
  4. package/dist/components/commands/dev.js +35 -8
  5. package/dist/components/commands/login.d.ts +2 -2
  6. package/dist/components/commands/login.js +110 -26
  7. package/dist/components/commands/preview.js +50 -11
  8. package/dist/components/commands/publish.js +23 -37
  9. package/dist/components/commands/{start copy.d.ts → skill.d.ts} +1 -1
  10. package/dist/components/commands/skill.js +230 -0
  11. package/dist/components/commands/{preview copy.d.ts → upgrade.d.ts} +1 -1
  12. package/dist/components/commands/upgrade.js +53 -0
  13. package/dist/lib/backend/publish-app.d.ts +1 -0
  14. package/dist/lib/backend/publish-app.js +26 -0
  15. package/dist/lib/backend-url.d.ts +1 -0
  16. package/dist/lib/backend-url.js +3 -0
  17. package/dist/lib/cli-flags.d.ts +18 -0
  18. package/dist/lib/cli-flags.js +18 -0
  19. package/dist/lib/manual.js +28 -0
  20. package/dist/lib/server/express.js +81 -41
  21. package/dist/lib/server/preview/backend/load-remote.cjs +28 -18
  22. package/dist/lib/server/utils.js +3 -3
  23. package/dist/lib/token.js +2 -3
  24. package/dist/lib/webpack/compile.d.ts +2 -0
  25. package/dist/lib/webpack/compile.js +30 -0
  26. package/dist/lib/webpack/configs/mf-client.d.ts +3 -0
  27. package/dist/lib/webpack/configs/mf-client.js +184 -0
  28. package/dist/lib/webpack/configs/mf-server.d.ts +2 -0
  29. package/dist/lib/webpack/configs/mf-server.js +463 -0
  30. package/dist/lib/webpack/configs/preview.d.ts +3 -0
  31. package/dist/lib/webpack/configs/preview.js +117 -0
  32. package/dist/lib/webpack/configs/utils.d.ts +10 -0
  33. package/dist/lib/webpack/configs/utils.js +172 -0
  34. package/dist/lib/webpack/dist/pregistered-actions.d.ts +2 -0
  35. package/dist/lib/webpack/dist/pulse.config.d.ts +7 -0
  36. package/dist/lib/webpack/dist/src/lib/agents/code-modifier-agent.d.ts +2 -0
  37. package/dist/lib/webpack/dist/src/lib/agents/vibe-coding-agent.d.ts +3 -0
  38. package/dist/lib/webpack/dist/src/lib/mcp/utils.d.ts +3 -0
  39. package/dist/lib/webpack/dist/src/lib/streaming/message-stream-controller.d.ts +10 -0
  40. package/dist/lib/webpack/dist/src/lib/types.d.ts +58 -0
  41. package/dist/lib/webpack/dist/src/server-function/generate-code/v1/generate.d.ts +5 -0
  42. package/dist/lib/webpack/dist/src/server-function/generate-code/v2/generate.d.ts +1 -0
  43. package/dist/lib/webpack/tsconfig.server.json +19 -0
  44. package/dist/lib/webpack/webpack-config.d.ts +1 -0
  45. package/dist/lib/webpack/webpack-config.js +24 -0
  46. package/dist/lib/webpack/webpack.config.d.ts +2 -0
  47. package/dist/lib/webpack/webpack.config.js +527 -0
  48. package/package.json +29 -20
  49. package/readme.md +7 -1
  50. package/dist/components/commands/preview copy.js +0 -14
  51. package/dist/components/commands/start copy.js +0 -14
  52. package/dist/lib/deps.d.ts +0 -1
  53. package/dist/lib/deps.js +0 -5
  54. package/dist/lib/node_module_bin.d.ts +0 -1
  55. package/dist/lib/node_module_bin.js +0 -7
  56. package/dist/lib/server/preview/backend/index.d.ts +0 -1
  57. package/dist/lib/server/preview/backend/index.js +0 -23
@@ -5,12 +5,15 @@ import { checkToken, getToken } from '../../lib/token.js';
5
5
  import Spinner from 'ink-spinner';
6
6
  import fs from 'fs';
7
7
  import { $ } from 'execa';
8
+ import { publishApp } from '../../lib/backend/publish-app.js';
8
9
  export default function Publish({ cli }) {
9
10
  const [isInProjectDir, setIsInProjectDir] = useState(false);
10
11
  const [isCheckingAuth, setIsCheckingAuth] = useState(true);
11
12
  const [isAuthenticated, setIsAuthenticated] = useState(false);
12
13
  const [isBuilding, setIsBuilding] = useState(false);
13
14
  const [isBuildingError, setIsBuildingError] = useState(false);
15
+ const [isZipping, setIsZipping] = useState(false);
16
+ const [isZippingError, setIsZippingError] = useState(false);
14
17
  const [isPublishing, setIsPublishing] = useState(false);
15
18
  const [isPublishingError, setIsPublishingError] = useState(false);
16
19
  const [isPublished, setIsPublished] = useState(false);
@@ -45,56 +48,39 @@ export default function Publish({ cli }) {
45
48
  // Build the extension
46
49
  useEffect(() => {
47
50
  async function buildExtension() {
48
- setIsBuilding(true);
49
- try {
50
- await $ `npm run build`;
51
- }
52
- catch (error) {
53
- setIsBuildingError(true);
54
- setIsBuilding(false);
55
- setFailureMessage('Build failed. Please run `npm run build` to see the error.');
56
- return;
51
+ if (cli.flags.build) {
52
+ setIsBuilding(true);
53
+ try {
54
+ await $ `npm run build`;
55
+ }
56
+ catch (error) {
57
+ setIsBuildingError(true);
58
+ setFailureMessage('Build failed. Please run `npm run build` to see the error.');
59
+ return;
60
+ }
61
+ finally {
62
+ setIsBuilding(false);
63
+ }
57
64
  }
65
+ setIsZipping(true);
58
66
  // Zip the dist folder
59
67
  try {
60
- await $({ cwd: 'dist' }) `zip -r ../node_modules/@pulse-editor/dist.zip *`;
68
+ await $({ cwd: 'dist' }) `zip -r ../node_modules/@pulse-editor/dist.zip .`;
61
69
  }
62
70
  catch (error) {
63
- setIsBuildingError(true);
64
- setIsBuilding(false);
65
- setFailureMessage('Failed to zip the build output.');
71
+ setIsZippingError(true);
72
+ setFailureMessage('Failed to zip the build output. ' + error.message);
66
73
  return;
67
74
  }
68
75
  finally {
69
- setIsBuilding(false);
76
+ setIsZipping(false);
70
77
  }
71
78
  await publishExtension();
72
79
  }
73
80
  async function publishExtension() {
74
81
  setIsPublishing(true);
75
- // Read pulse.config.json for visibility
76
- const config = JSON.parse(fs.readFileSync('./dist/client/pulse.config.json', 'utf-8'));
77
- const visibility = config.visibility;
78
- // Upload the zip file to the server
79
82
  try {
80
- const formData = new FormData();
81
- const buffer = fs.readFileSync('./node_modules/@pulse-editor/dist.zip');
82
- // @ts-ignore Create a Blob from the buffer
83
- const blob = new Blob([buffer], {
84
- type: 'application/zip',
85
- });
86
- formData.append('file', blob, 'dist.zip');
87
- formData.append('visibility', visibility);
88
- // Send the file to the server
89
- const res = await fetch(cli.flags.stage
90
- ? 'https://localhost:8080/api/app/publish'
91
- : 'https://pulse-editor.com/api/app/publish', {
92
- method: 'POST',
93
- headers: {
94
- Authorization: `Bearer ${getToken(cli.flags.stage)}`,
95
- },
96
- body: formData,
97
- });
83
+ const res = await publishApp(cli.flags.stage);
98
84
  if (res.status === 200) {
99
85
  setIsPublished(true);
100
86
  }
@@ -122,5 +108,5 @@ export default function Publish({ cli }) {
122
108
  buildExtension();
123
109
  }
124
110
  }, [isAuthenticated]);
125
- return (_jsx(_Fragment, { children: !isInProjectDir ? (_jsx(Text, { color: 'redBright', children: "\u26D4 The current directory does not contain a Pulse Editor project." })) : isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : isAuthenticated ? (_jsxs(_Fragment, { children: [isBuilding && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Building..." })] })), isBuildingError && (_jsx(Text, { color: 'redBright', children: "\u274C Error building the extension. Please run `npm run build` to see the error." })), isPublishing && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Publishing..." })] })), isPublishingError && (_jsxs(_Fragment, { children: [_jsx(Text, { color: 'redBright', children: "\u274C Failed to publish extension." }), failureMessage && (_jsxs(Text, { color: 'redBright', children: ["Error: ", failureMessage] }))] })), isPublished && (_jsx(Text, { color: 'greenBright', children: "\u2705 Extension published successfully." }))] })) : (_jsxs(Text, { children: ["You are not authenticated or your access token is invalid. Publishing to Extension Marketplace is in Beta access. Please visit", _jsx(Text, { color: 'blueBright', children: " https://pulse-editor.com/beta " }), "to apply for Beta access."] })) }));
111
+ return (_jsx(_Fragment, { children: !isInProjectDir ? (_jsx(Text, { color: 'redBright', children: "\u26D4 The current directory does not contain a Pulse Editor project." })) : isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : !isAuthenticated ? (_jsxs(Text, { children: ["You are not authenticated or your access token is invalid. Publishing to Extension Marketplace is in Beta access. Please visit", _jsx(Text, { color: 'blueBright', children: " https://pulse-editor.com/beta " }), "to apply for Beta access."] })) : (_jsxs(_Fragment, { children: [isBuilding && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Building..." })] })), isBuildingError && (_jsx(Text, { color: 'redBright', children: "\u274C Error building the extension. Please run `npm run build` to see the error." })), isZipping && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Compressing build..." })] })), isZippingError && (_jsxs(Text, { color: 'redBright', children: ["\u274C Error zipping the build output. ", failureMessage] })), isPublishing && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Publishing..." })] })), isPublishingError && (_jsxs(_Fragment, { children: [_jsx(Text, { color: 'redBright', children: "\u274C Failed to publish extension." }), failureMessage && (_jsxs(Text, { color: 'redBright', children: ["Error: ", failureMessage] }))] })), isPublished && (_jsx(Text, { color: 'greenBright', children: "\u2705 Extension published successfully." }))] })) }));
126
112
  }
@@ -1,5 +1,5 @@
1
1
  import { Result } from 'meow';
2
2
  import { Flags } from '../../lib/cli-flags.js';
3
- export default function Start({ cli }: {
3
+ export default function Skill({ cli }: {
4
4
  cli: Result<Flags>;
5
5
  }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,230 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Box, Text, useInput } from 'ink';
4
+ import Spinner from 'ink-spinner';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { checkToken, getToken } from '../../lib/token.js';
8
+ import { getBackendUrl } from '../../lib/backend-url.js';
9
+ // ---------------------------------------------------------------------------
10
+ // MultilineInput
11
+ // ---------------------------------------------------------------------------
12
+ function MultilineInput({ onSubmit, focus, }) {
13
+ const [lines, setLines] = useState(['']);
14
+ useInput((input, key) => {
15
+ // Regular Enter (\r) → submit
16
+ // Shift+Enter → terminals send \n (0x0A) rather than setting key.shift+key.return
17
+ if (input === '\n') {
18
+ setLines(prev => [...prev, '']);
19
+ return;
20
+ }
21
+ if (key.return) {
22
+ const value = lines.join('\n').trim();
23
+ if (value)
24
+ onSubmit(value);
25
+ return;
26
+ }
27
+ if (key.backspace || key.delete) {
28
+ setLines(prev => {
29
+ const next = [...prev];
30
+ const last = next[next.length - 1];
31
+ if (last.length > 0) {
32
+ next[next.length - 1] = last.slice(0, -1);
33
+ }
34
+ else if (next.length > 1) {
35
+ next.pop();
36
+ }
37
+ return next;
38
+ });
39
+ return;
40
+ }
41
+ if (input) {
42
+ setLines(prev => {
43
+ const next = [...prev];
44
+ next[next.length - 1] += input;
45
+ return next;
46
+ });
47
+ }
48
+ }, { isActive: focus });
49
+ return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: lines.map((line, i) => (_jsxs(Text, { children: [line, i === lines.length - 1 ? _jsx(Text, { backgroundColor: "white", children: " " }) : ''] }, i))) }));
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // SkillCreate
53
+ // ---------------------------------------------------------------------------
54
+ function SkillCreate({ cli }) {
55
+ const [skillName, setSkillName] = useState(cli.input[2]);
56
+ const [description, setDescription] = useState(cli.flags.description);
57
+ const [status, setStatus] = useState();
58
+ const [errorMessage, setErrorMessage] = useState('');
59
+ const [chunkCount, setChunkCount] = useState(0);
60
+ // Once both fields are collected, authenticate
61
+ useEffect(() => {
62
+ if (!skillName || !description)
63
+ return;
64
+ setStatus('authenticating');
65
+ }, [skillName, description]);
66
+ useEffect(() => {
67
+ if (status !== 'authenticating')
68
+ return;
69
+ async function authenticate() {
70
+ const token = getToken(cli.flags.stage);
71
+ if (token && (await checkToken(token, cli.flags.stage))) {
72
+ setStatus('generating');
73
+ }
74
+ else {
75
+ setErrorMessage('You are not authenticated. Please run pulse login first.');
76
+ setStatus('error');
77
+ setTimeout(() => process.exit(1), 0);
78
+ }
79
+ }
80
+ authenticate();
81
+ }, [status]);
82
+ useEffect(() => {
83
+ if (status !== 'generating' || !skillName || !description)
84
+ return;
85
+ async function generate() {
86
+ const token = getToken(cli.flags.stage);
87
+ const backendUrl = getBackendUrl(cli.flags.stage);
88
+ try {
89
+ const res = await fetch(`${backendUrl}/api/inference/cli/skill/create`, {
90
+ method: 'POST',
91
+ headers: {
92
+ 'Content-Type': 'application/json',
93
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
94
+ },
95
+ body: JSON.stringify({ description }),
96
+ });
97
+ if (!res.ok) {
98
+ setErrorMessage(`Server returned error code ${res.status}.`);
99
+ setStatus('error');
100
+ setTimeout(() => process.exit(), 0);
101
+ return;
102
+ }
103
+ const reader = res.body?.getReader();
104
+ const decoder = new TextDecoder();
105
+ let code = '';
106
+ if (reader) {
107
+ while (true) {
108
+ const { done, value } = await reader.read();
109
+ if (done)
110
+ break;
111
+ code += decoder.decode(value, { stream: true });
112
+ setChunkCount(n => n + 1);
113
+ }
114
+ }
115
+ const skillDir = path.join(process.cwd(), 'src', 'skill', skillName);
116
+ fs.mkdirSync(skillDir, { recursive: true });
117
+ fs.writeFileSync(path.join(skillDir, 'action.ts'), code, 'utf-8');
118
+ setStatus('done');
119
+ setTimeout(() => process.exit(), 0);
120
+ }
121
+ catch (err) {
122
+ setErrorMessage(err?.message ?? String(err));
123
+ setStatus('error');
124
+ setTimeout(() => process.exit(), 0);
125
+ }
126
+ }
127
+ generate();
128
+ }, [status]);
129
+ return (_jsxs(_Fragment, { children: [!skillName && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Skill name:" }), _jsx(MultilineInput, { onSubmit: value => setTimeout(() => setSkillName(value), 0), focus: !skillName })] })), skillName && !description && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["What should this skill do?", ' ', _jsx(Text, { color: "blueBright", children: "(Shift+Enter for newline, Enter to confirm)" })] }), _jsx(MultilineInput, { onSubmit: value => setTimeout(() => setDescription(value), 0), focus: !!skillName && !description })] })), status === 'authenticating' && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })), status === 'generating' && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsxs(Text, { children: [" Generating skill action for \"", skillName, "\"... "] }), _jsxs(Text, { color: "blueBright", children: ["[", chunkCount, " chunks received]"] })] })), status === 'done' && (_jsxs(Text, { color: "greenBright", children: ["\u2705 Skill action created at src/skill/", skillName, "/action.ts"] })), status === 'error' && (_jsxs(Text, { color: "redBright", children: ["\u274C ", errorMessage] }))] }));
130
+ }
131
+ // ---------------------------------------------------------------------------
132
+ // SkillFix
133
+ // ---------------------------------------------------------------------------
134
+ function SkillFix({ cli }) {
135
+ const [skillName, setSkillName] = useState(cli.input[2]);
136
+ const [status, setStatus] = useState();
137
+ const [errorMessage, setErrorMessage] = useState('');
138
+ const [chunkCount, setChunkCount] = useState(0);
139
+ // Once skill name is collected, authenticate
140
+ useEffect(() => {
141
+ if (!skillName)
142
+ return;
143
+ setStatus('authenticating');
144
+ }, [skillName]);
145
+ useEffect(() => {
146
+ if (status !== 'authenticating')
147
+ return;
148
+ async function authenticate() {
149
+ const token = getToken(cli.flags.stage);
150
+ if (token && (await checkToken(token, cli.flags.stage))) {
151
+ // Validate file exists before fixing
152
+ const actionPath = path.join(process.cwd(), 'src', 'skill', skillName, 'action.ts');
153
+ if (!fs.existsSync(actionPath)) {
154
+ setErrorMessage(`Action file not found: src/skill/${skillName}/action.ts`);
155
+ setStatus('error');
156
+ setTimeout(() => process.exit(), 0);
157
+ return;
158
+ }
159
+ setStatus('fixing');
160
+ }
161
+ else {
162
+ setErrorMessage('You are not authenticated. Please run pulse login first.');
163
+ setStatus('error');
164
+ setTimeout(() => process.exit(1), 0);
165
+ }
166
+ }
167
+ authenticate();
168
+ }, [status]);
169
+ useEffect(() => {
170
+ if (status !== 'fixing' || !skillName)
171
+ return;
172
+ async function fix() {
173
+ const actionPath = path.join(process.cwd(), 'src', 'skill', skillName, 'action.ts');
174
+ const code = fs.readFileSync(actionPath, 'utf-8');
175
+ const token = getToken(cli.flags.stage);
176
+ const backendUrl = getBackendUrl(cli.flags.stage);
177
+ try {
178
+ const res = await fetch(`${backendUrl}/api/inference/cli/skill/fix`, {
179
+ method: 'POST',
180
+ headers: {
181
+ 'Content-Type': 'application/json',
182
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
183
+ },
184
+ body: JSON.stringify({ code }),
185
+ });
186
+ if (!res.ok) {
187
+ setErrorMessage(`Server returned error code ${res.status}.`);
188
+ setStatus('error');
189
+ setTimeout(() => process.exit(), 0);
190
+ return;
191
+ }
192
+ const reader = res.body?.getReader();
193
+ const decoder = new TextDecoder();
194
+ let fixed = '';
195
+ if (reader) {
196
+ while (true) {
197
+ const { done, value } = await reader.read();
198
+ if (done)
199
+ break;
200
+ fixed += decoder.decode(value, { stream: true });
201
+ setChunkCount(n => n + 1);
202
+ }
203
+ }
204
+ fs.writeFileSync(actionPath, fixed, 'utf-8');
205
+ setStatus('done');
206
+ setTimeout(() => process.exit(), 0);
207
+ }
208
+ catch (err) {
209
+ setErrorMessage(err?.message ?? String(err));
210
+ setStatus('error');
211
+ setTimeout(() => process.exit(), 0);
212
+ }
213
+ }
214
+ fix();
215
+ }, [status]);
216
+ return (_jsxs(_Fragment, { children: [!skillName && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Action name:" }), _jsx(MultilineInput, { onSubmit: value => setTimeout(() => setSkillName(value), 0), focus: !skillName })] })), status === 'authenticating' && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })), status === 'fixing' && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsxs(Text, { children: [" Fixing JSDoc for skill \"", skillName, "\"... "] }), _jsxs(Text, { color: "blueBright", children: ["[", chunkCount, " chunks received]"] })] })), status === 'done' && (_jsxs(Text, { color: "greenBright", children: ["\u2705 JSDoc fixed and saved to src/skill/", skillName, "/action.ts"] })), status === 'error' && (_jsxs(Text, { color: "redBright", children: ["\u274C ", errorMessage] }))] }));
217
+ }
218
+ // ---------------------------------------------------------------------------
219
+ // Skill (top-level router)
220
+ // ---------------------------------------------------------------------------
221
+ export default function Skill({ cli }) {
222
+ const subCommand = cli.input[1];
223
+ if (subCommand === 'create') {
224
+ return _jsx(SkillCreate, { cli: cli });
225
+ }
226
+ if (subCommand === 'fix') {
227
+ return _jsx(SkillFix, { cli: cli });
228
+ }
229
+ return (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "redBright", children: ["Unknown subcommand: ", subCommand ?? '(none)'] }), _jsxs(Text, { children: ["Available subcommands:", '\n', ' ', "pulse skill create ", '<skill-name>', " --description \"", '<description>', "\"", '\n', ' ', "pulse skill fix ", '<action-name>'] })] }));
230
+ }
@@ -1,5 +1,5 @@
1
1
  import { Result } from 'meow';
2
2
  import { Flags } from '../../lib/cli-flags.js';
3
- export default function Preview({ cli }: {
3
+ export default function Upgrade({ cli }: {
4
4
  cli: Result<Flags>;
5
5
  }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,53 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { $ } from 'execa';
3
+ import React, { useEffect, useState } from 'react';
4
+ import { Box, Text } from 'ink';
5
+ import Spinner from 'ink-spinner';
6
+ import fs from 'fs';
7
+ export default function Upgrade({ cli }) {
8
+ const [isInProjectDir, setIsInProjectDir] = useState(false);
9
+ const [step, setStep] = useState('check-config');
10
+ const [isError, setIsError] = useState(false);
11
+ const [errorMessage, setErrorMessage] = useState(null);
12
+ useEffect(() => {
13
+ async function checkConfig() {
14
+ // Check if the current dir contains pulse.config.ts
15
+ const currentDir = process.cwd();
16
+ const pulseConfigPath = `${currentDir}/pulse.config.ts`;
17
+ if (fs.existsSync(pulseConfigPath)) {
18
+ setIsInProjectDir(true);
19
+ }
20
+ setStep('upgrade');
21
+ }
22
+ checkConfig();
23
+ }, []);
24
+ useEffect(() => {
25
+ async function start() {
26
+ try {
27
+ await upgradePackages();
28
+ }
29
+ catch (error) {
30
+ setIsError(true);
31
+ setErrorMessage(error.message);
32
+ }
33
+ }
34
+ if (isInProjectDir) {
35
+ start();
36
+ }
37
+ }, [isInProjectDir]);
38
+ async function upgradePackages() {
39
+ const tag = cli.flags.beta ? 'beta' : 'latest';
40
+ await $ `npm install react@${React.version} react-dom@${React.version} --save-exact --silent --force`;
41
+ await $ `npm install -D @pulse-editor/cli@${tag} --silent --force`;
42
+ await $ `npm install @pulse-editor/shared-utils@${tag} @pulse-editor/react-api@${tag} --silent --force`;
43
+ // Remove webpack.config.ts if exists
44
+ const webpackConfigPath = `${process.cwd()}/webpack.config.ts`;
45
+ if (fs.existsSync(webpackConfigPath)) {
46
+ fs.unlinkSync(webpackConfigPath);
47
+ }
48
+ // Uninstall @module-federation/node html-webpack-plugin copy-webpack-plugin glob mini-css-extract-plugin webpack webpack-cli webpack-dev-server
49
+ await $ `npm uninstall @module-federation/node html-webpack-plugin copy-webpack-plugin glob mini-css-extract-plugin webpack webpack-cli webpack-dev-server --silent --force`;
50
+ setStep('done');
51
+ }
52
+ return (_jsx(_Fragment, { children: isError ? (_jsxs(Text, { color: 'redBright', children: ["\u274C An error occurred: ", errorMessage || 'Unknown error'] })) : !isInProjectDir ? (_jsx(Text, { color: 'redBright', children: "\u26D4 The current directory does not contain a Pulse Editor project." })) : step === 'check-config' ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking configuration..." })] })) : step === 'upgrade' ? (_jsx(_Fragment, { children: _jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Upgrading packages..." })] }) })) : (_jsx(Box, { children: _jsx(Text, { color: 'greenBright', children: "\u2705 Upgrade completed successfully." }) })) }));
53
+ }
@@ -0,0 +1 @@
1
+ export declare function publishApp(isStage: boolean): Promise<Response>;
@@ -0,0 +1,26 @@
1
+ import { getToken } from '../token.js';
2
+ import { getBackendUrl } from '../backend-url.js';
3
+ import fs from 'fs';
4
+ export async function publishApp(isStage) {
5
+ // Upload the zip file to the server
6
+ // Read pulse.config.json for visibility
7
+ const config = JSON.parse(fs.readFileSync('./dist/pulse.config.json', 'utf-8'));
8
+ const visibility = config.visibility;
9
+ const formData = new FormData();
10
+ const buffer = fs.readFileSync('./node_modules/@pulse-editor/dist.zip');
11
+ // @ts-ignore Create a Blob from the buffer
12
+ const blob = new Blob([buffer], {
13
+ type: 'application/zip',
14
+ });
15
+ formData.append('file', blob, 'dist.zip');
16
+ formData.append('visibility', visibility);
17
+ // Send the file to the server
18
+ const res = await fetch(`${getBackendUrl(isStage)}/api/app/publish`, {
19
+ method: 'POST',
20
+ headers: {
21
+ Authorization: `Bearer ${getToken(isStage)}`,
22
+ },
23
+ body: formData,
24
+ });
25
+ return res;
26
+ }
@@ -0,0 +1 @@
1
+ export declare function getBackendUrl(stage: boolean): "https://localhost:8080" | "https://pulse-editor.com";
@@ -0,0 +1,3 @@
1
+ export function getBackendUrl(stage) {
2
+ return stage ? 'https://localhost:8080' : 'https://pulse-editor.com';
3
+ }
@@ -25,5 +25,23 @@ export declare const flags: {
25
25
  type: "string";
26
26
  shortFlag: string;
27
27
  };
28
+ beta: {
29
+ type: "boolean";
30
+ };
31
+ build: {
32
+ type: "boolean";
33
+ default: true;
34
+ };
35
+ path: {
36
+ type: "string";
37
+ shortFlag: string;
38
+ };
39
+ displayName: {
40
+ type: "string";
41
+ };
42
+ description: {
43
+ type: "string";
44
+ shortFlag: string;
45
+ };
28
46
  };
29
47
  export type Flags = typeof flags;
@@ -29,4 +29,22 @@ export const flags = defineFlags({
29
29
  type: 'string',
30
30
  shortFlag: 't',
31
31
  },
32
+ beta: {
33
+ type: 'boolean',
34
+ },
35
+ build: {
36
+ type: 'boolean',
37
+ default: true,
38
+ },
39
+ path: {
40
+ type: 'string',
41
+ shortFlag: 'p',
42
+ },
43
+ displayName: {
44
+ type: 'string',
45
+ },
46
+ description: {
47
+ type: 'string',
48
+ shortFlag: 'd',
49
+ },
32
50
  });
@@ -27,6 +27,9 @@ const logout = `\
27
27
  `;
28
28
  const publish = `\
29
29
  publish Publish Pulse Editor Extension in current directory to the Pulse Editor Platform.
30
+ Flags:
31
+ --noBuild
32
+ Skip the build step before publishing.
30
33
 
31
34
  `;
32
35
  const create = `\
@@ -41,6 +44,9 @@ const create = `\
41
44
  --visibility, -v [visibility]
42
45
  The visibility of the new project. Options are private,
43
46
  public, and unlisted.
47
+ --path, -p [path]
48
+ The path where to create the new project. Defaults to
49
+ the name of the project in the current working directory.
44
50
 
45
51
  `;
46
52
  const preview = `\
@@ -71,6 +77,26 @@ const start = `\
71
77
  const clean = `\
72
78
  clean Clean the dist/ directory.
73
79
 
80
+ `;
81
+ const upgrade = `\
82
+ upgrade Upgrade Pulse Editor CLI and related packages to the latest version.
83
+ Flags:
84
+ --beta
85
+ Upgrade to the latest beta version.
86
+
87
+ `;
88
+ const skill = `\
89
+ skill Manage skill actions for the current Pulse App.
90
+
91
+ Subcommands:
92
+ create <skill-name> --description "<description>"
93
+ Generate a new skill action using AI and write it to
94
+ src/skill/<skill-name>/action.ts.
95
+
96
+ fix <action-name>
97
+ Fix and apply valid JSDoc comments to an existing skill
98
+ action at src/skill/<action-name>/action.ts using AI.
99
+
74
100
  `;
75
101
  export const commandsManual = {
76
102
  help,
@@ -84,4 +110,6 @@ export const commandsManual = {
84
110
  build,
85
111
  start,
86
112
  clean,
113
+ upgrade,
114
+ skill,
87
115
  };