@pulse-editor/cli 0.0.1 → 0.1.0-beta.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/app.d.ts +6 -0
- package/dist/app.js +21 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +22 -0
- package/dist/components/commands/chat.d.ts +6 -0
- package/dist/components/commands/chat.js +9 -0
- package/dist/components/commands/create.d.ts +6 -0
- package/dist/components/commands/create.js +93 -0
- package/dist/components/commands/help.d.ts +6 -0
- package/dist/components/commands/help.js +10 -0
- package/dist/components/commands/login.d.ts +6 -0
- package/dist/components/commands/login.js +108 -0
- package/dist/components/commands/logout.d.ts +6 -0
- package/dist/components/commands/logout.js +14 -0
- package/dist/components/commands/publish.d.ts +6 -0
- package/dist/components/commands/publish.js +125 -0
- package/dist/components/header.d.ts +2 -0
- package/dist/components/header.js +8 -0
- package/dist/lib/cli-flags.d.ts +17 -0
- package/dist/lib/cli-flags.js +20 -0
- package/dist/lib/manual.d.ts +1 -0
- package/dist/lib/manual.js +44 -0
- package/dist/lib/token.d.ts +4 -0
- package/dist/lib/token.js +65 -0
- package/dist/lib/types.d.ts +5 -0
- package/dist/lib/types.js +1 -0
- package/package.json +1 -1
- package/readme.md +32 -14
package/dist/app.d.ts
ADDED
package/dist/app.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import Login from './components/commands/login.js';
|
|
3
|
+
import Publish from './components/commands/publish.js';
|
|
4
|
+
import Help from './components/commands/help.js';
|
|
5
|
+
import Chat from './components/commands/chat.js';
|
|
6
|
+
import Logout from './components/commands/logout.js';
|
|
7
|
+
import Create from './components/commands/create.js';
|
|
8
|
+
export default function App({ cli }) {
|
|
9
|
+
const [command, setCommand] = useState(undefined);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const cmd = cli.input[0] ?? 'help';
|
|
12
|
+
setCommand(cmd);
|
|
13
|
+
}, [cli.input]);
|
|
14
|
+
return (React.createElement(React.Fragment, null,
|
|
15
|
+
command === 'help' && React.createElement(Help, { cli: cli }),
|
|
16
|
+
command === 'chat' && React.createElement(Chat, { cli: cli }),
|
|
17
|
+
command === 'login' && React.createElement(Login, { cli: cli }),
|
|
18
|
+
command === 'logout' && React.createElement(Logout, { cli: cli }),
|
|
19
|
+
command === 'publish' && React.createElement(Publish, { cli: cli }),
|
|
20
|
+
command === 'create' && React.createElement(Create, { cli: cli })));
|
|
21
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import meow from 'meow';
|
|
5
|
+
import App from './app.js';
|
|
6
|
+
import { commandsManual } from './lib/manual.js';
|
|
7
|
+
import { flags } from './lib/cli-flags.js';
|
|
8
|
+
const cli = meow(`\
|
|
9
|
+
Usage
|
|
10
|
+
pulse [command] [flags]
|
|
11
|
+
|
|
12
|
+
Commands
|
|
13
|
+
${Object.entries(commandsManual)
|
|
14
|
+
.map(([_, description]) => `${description}`)
|
|
15
|
+
.join('')}
|
|
16
|
+
Examples
|
|
17
|
+
pulse help publish
|
|
18
|
+
`, {
|
|
19
|
+
importMeta: import.meta,
|
|
20
|
+
flags: flags,
|
|
21
|
+
});
|
|
22
|
+
render(React.createElement(App, { cli: cli }));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
export default function Chat({ cli }) {
|
|
4
|
+
const message = cli.input[1];
|
|
5
|
+
return (React.createElement(React.Fragment, null,
|
|
6
|
+
React.createElement(Text, null,
|
|
7
|
+
"Hello, ",
|
|
8
|
+
React.createElement(Text, { color: "green" }, message))));
|
|
9
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import Spinner from 'ink-spinner';
|
|
4
|
+
import { $ } from 'execa';
|
|
5
|
+
import SelectInput from 'ink-select-input';
|
|
6
|
+
import { UncontrolledTextInput } from 'ink-text-input';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
export default function Create({ cli }) {
|
|
10
|
+
const [framework, setFramework] = useState(undefined);
|
|
11
|
+
const [projectName, setProjectName] = useState(undefined);
|
|
12
|
+
const [isCreated, setIsCreated] = useState(false);
|
|
13
|
+
const [isFrameworkSelected, setIsFrameworkSelected] = useState(false);
|
|
14
|
+
const [isPathValid, setIsPathValid] = useState(true);
|
|
15
|
+
const [isCloneFailed, setIsCloneFailed] = useState(false);
|
|
16
|
+
const frameworkItems = [
|
|
17
|
+
{
|
|
18
|
+
label: 'React',
|
|
19
|
+
value: 'react',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: 'Modern.js (WIP)',
|
|
23
|
+
value: 'modernjs',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Vue (WIP)',
|
|
27
|
+
value: 'vue',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: 'Angular (WIP)',
|
|
31
|
+
value: 'angular',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const framework = cli.flags.framework;
|
|
36
|
+
setFramework(framework);
|
|
37
|
+
}, [cli]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
async function createFromTemplate(name) {
|
|
40
|
+
if (framework === 'react') {
|
|
41
|
+
// Clone the template repository
|
|
42
|
+
try {
|
|
43
|
+
await $ `git clone https://github.com/ClayPulse/pulse-editor-extension-template.git ${name}`;
|
|
44
|
+
setIsCreated(true);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
setIsCloneFailed(true);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Modify the package.json file to update the name
|
|
51
|
+
const packageJsonPath = path.join(process.cwd(), name, 'package.json');
|
|
52
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
53
|
+
packageJson.name = name;
|
|
54
|
+
// Write the modified package.json back to the file
|
|
55
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (projectName) {
|
|
59
|
+
// Check if the project already exists
|
|
60
|
+
const projectPath = path.join(process.cwd(), projectName);
|
|
61
|
+
if (fs.existsSync(projectPath)) {
|
|
62
|
+
setIsPathValid(false);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
createFromTemplate(projectName);
|
|
66
|
+
}
|
|
67
|
+
}, [projectName]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
setIsFrameworkSelected(framework !== undefined);
|
|
71
|
+
}, 0);
|
|
72
|
+
}, [framework]);
|
|
73
|
+
return (React.createElement(React.Fragment, null,
|
|
74
|
+
!cli.flags.framework && (React.createElement(React.Fragment, null,
|
|
75
|
+
React.createElement(Text, null, "\uD83D\uDEA9Create a new Pulse Editor app using your favorite web framework!"),
|
|
76
|
+
React.createElement(SelectInput, { items: frameworkItems, onSelect: item => {
|
|
77
|
+
setFramework(item.value);
|
|
78
|
+
}, isFocused: framework === undefined }),
|
|
79
|
+
React.createElement(Text, null, " "))),
|
|
80
|
+
isFrameworkSelected && (React.createElement(React.Fragment, null,
|
|
81
|
+
React.createElement(Box, null,
|
|
82
|
+
React.createElement(Text, null, "Enter your project name: "),
|
|
83
|
+
React.createElement(UncontrolledTextInput, { onSubmit: value => setProjectName(value), focus: projectName === undefined })),
|
|
84
|
+
projectName && (React.createElement(React.Fragment, null,
|
|
85
|
+
framework === 'react' &&
|
|
86
|
+
(!isPathValid ? (React.createElement(Text, { color: "redBright" }, "\u274C A project with same name already exists in current path.")) : isCloneFailed ? (React.createElement(Text, { color: "redBright" }, "\u274C Failed to clone the template. Please check your internet connection and try again.")) : isCreated ? (React.createElement(Text, null, "\uD83D\uDE80 Pulse Editor React app project created successfully!")) : (React.createElement(React.Fragment, null,
|
|
87
|
+
React.createElement(Box, null,
|
|
88
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
89
|
+
React.createElement(Text, null,
|
|
90
|
+
' ',
|
|
91
|
+
"Creating a new Pulse Editor app using React template..."))))),
|
|
92
|
+
framework !== 'react' && (React.createElement(Text, null, "\uD83D\uDEA7 Currently not available. We'd like to invite you to work on these frameworks if you are interested in! Check out our tutorial to integrate your favorite web framework with Pulse Editor using Module Federation."))))))));
|
|
93
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { commandsManual } from '../../lib/manual.js';
|
|
4
|
+
import Header from '../header.js';
|
|
5
|
+
export default function Help({ cli }) {
|
|
6
|
+
const subCommand = cli.input[1];
|
|
7
|
+
return (React.createElement(React.Fragment, null, subCommand ? (React.createElement(Text, null, commandsManual[subCommand])) : (React.createElement(React.Fragment, null,
|
|
8
|
+
React.createElement(Header, null),
|
|
9
|
+
React.createElement(Text, null, cli.help)))));
|
|
10
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Box, Text, useApp } from 'ink';
|
|
3
|
+
import SelectInput from 'ink-select-input';
|
|
4
|
+
import TextInput from 'ink-text-input';
|
|
5
|
+
import Spinner from 'ink-spinner';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { checkToken, getToken, isTokenInEnv, saveToken, } from '../../lib/token.js';
|
|
9
|
+
export default function Login({ cli }) {
|
|
10
|
+
const [loginMethod, setLoginMethod] = useState(undefined);
|
|
11
|
+
const [isShowLoginMethod, setIsShowLoginMethod] = useState(false);
|
|
12
|
+
const [isMethodSelected, setIsMethodSelected] = useState(false);
|
|
13
|
+
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
|
|
14
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
15
|
+
const [token, setToken] = useState('');
|
|
16
|
+
const [isTokenSaved, setIsTokenSaved] = useState(false);
|
|
17
|
+
const [tokenInput, setTokenInput] = useState('');
|
|
18
|
+
const [saveTokenInput, setSaveTokenInput] = useState('');
|
|
19
|
+
const loginMethodItems = [
|
|
20
|
+
{
|
|
21
|
+
label: 'Login using access token',
|
|
22
|
+
value: 'token',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: '(WIP) Login in browser',
|
|
26
|
+
value: 'flow',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
const { exit } = useApp();
|
|
30
|
+
// Check login method
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const savedToken = getToken();
|
|
33
|
+
setIsShowLoginMethod(!savedToken && !cli.flags.token && !cli.flags.flow);
|
|
34
|
+
if (savedToken) {
|
|
35
|
+
setLoginMethod('token');
|
|
36
|
+
setToken(savedToken);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
else if (cli.flags.token) {
|
|
40
|
+
setLoginMethod('token');
|
|
41
|
+
}
|
|
42
|
+
else if (cli.flags.flow) {
|
|
43
|
+
setLoginMethod('flow');
|
|
44
|
+
}
|
|
45
|
+
}, [cli]);
|
|
46
|
+
// Check token validity
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
// Only check token validity when it is set
|
|
49
|
+
if (loginMethod === 'token' && token.length > 0) {
|
|
50
|
+
checkToken(token, cli.flags.dev).then(isValid => {
|
|
51
|
+
setIsAuthenticated(isValid);
|
|
52
|
+
setIsCheckingAuth(false);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}, [token, loginMethod]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
setIsMethodSelected(loginMethod !== undefined);
|
|
59
|
+
}, 0);
|
|
60
|
+
}, [loginMethod]);
|
|
61
|
+
return (React.createElement(React.Fragment, null,
|
|
62
|
+
isShowLoginMethod && (React.createElement(React.Fragment, null,
|
|
63
|
+
React.createElement(Text, null, "Login to the Pulse Editor Platform"),
|
|
64
|
+
React.createElement(SelectInput, { items: loginMethodItems, onSelect: item => {
|
|
65
|
+
setLoginMethod(item.value);
|
|
66
|
+
}, isFocused: loginMethod === undefined }),
|
|
67
|
+
React.createElement(Text, null, " "))),
|
|
68
|
+
isMethodSelected &&
|
|
69
|
+
loginMethod === 'token' &&
|
|
70
|
+
(token.length === 0 ? (React.createElement(React.Fragment, null,
|
|
71
|
+
React.createElement(Text, null, "Enter your Pulse Editor access token:"),
|
|
72
|
+
React.createElement(TextInput, { mask: "*", value: tokenInput, onChange: setTokenInput, onSubmit: value => {
|
|
73
|
+
if (value.length === 0) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
setToken(value);
|
|
77
|
+
} }))) : isCheckingAuth ? (React.createElement(Box, null,
|
|
78
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
79
|
+
React.createElement(Text, null, " Checking authentication..."))) : isAuthenticated ? (React.createElement(React.Fragment, null,
|
|
80
|
+
React.createElement(Text, null, "\u2705 You are signed in successfully."),
|
|
81
|
+
!isTokenInEnv() && getToken() !== token && (React.createElement(React.Fragment, null,
|
|
82
|
+
React.createElement(Text, null, "\uD83D\uDFE2 It is recommended to save your access token as an environment variable PE_ACCESS_TOKEN."),
|
|
83
|
+
React.createElement(Box, null,
|
|
84
|
+
React.createElement(Text, null,
|
|
85
|
+
"\u26A0\uFE0F (NOT recommended) Or, do you want to save access token to user home directory? (y/n)",
|
|
86
|
+
' '),
|
|
87
|
+
React.createElement(TextInput, { value: saveTokenInput, onChange: setSaveTokenInput, onSubmit: value => {
|
|
88
|
+
if (value.length === 0) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (value === 'y') {
|
|
92
|
+
saveToken(token);
|
|
93
|
+
setIsTokenSaved(true);
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
exit();
|
|
96
|
+
}, 0);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
exit();
|
|
100
|
+
}
|
|
101
|
+
} })))),
|
|
102
|
+
isTokenSaved && (React.createElement(Text, null,
|
|
103
|
+
"Token saved to ",
|
|
104
|
+
path.join(os.homedir(), '.pulse-editor'))))) : (React.createElement(Text, null, "Authentication error: please enter valid credentials."))),
|
|
105
|
+
isMethodSelected && loginMethod === 'flow' && (React.createElement(React.Fragment, null,
|
|
106
|
+
React.createElement(Text, null, "(WIP) Open the following URL in your browser:"),
|
|
107
|
+
React.createElement(Text, null, "https://pulse-editor.com/login")))));
|
|
108
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { saveToken } from '../../lib/token.js';
|
|
4
|
+
import Spinner from 'ink-spinner';
|
|
5
|
+
export default function Logout({ cli }) {
|
|
6
|
+
const [isLoggedOut, setIsLoggedOut] = useState(false);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
saveToken(undefined);
|
|
9
|
+
setIsLoggedOut(true);
|
|
10
|
+
}, []);
|
|
11
|
+
return (React.createElement(React.Fragment, null, isLoggedOut ? (React.createElement(Text, null, "\uD83D\uDE80 Successfully logged out!")) : (React.createElement(Box, null,
|
|
12
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
13
|
+
React.createElement(Text, null, " Logging out...")))));
|
|
14
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { checkToken, getToken } from '../../lib/token.js';
|
|
4
|
+
import Spinner from 'ink-spinner';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { $ } from 'execa';
|
|
7
|
+
export default function Publish({ cli }) {
|
|
8
|
+
const [isInProjectDir, setIsInProjectDir] = useState(false);
|
|
9
|
+
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
|
|
10
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
11
|
+
const [isBuilding, setIsBuilding] = useState(false);
|
|
12
|
+
const [isBuildingError, setIsBuildingError] = useState(false);
|
|
13
|
+
const [isPublishing, setIsPublishing] = useState(false);
|
|
14
|
+
const [isPublishingError, setIsPublishingError] = useState(false);
|
|
15
|
+
const [isPublished, setIsPublished] = useState(false);
|
|
16
|
+
const [failureMessage, setFailureMessage] = useState(undefined);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// Check if the current dir contains pulse.config.ts
|
|
19
|
+
const currentDir = process.cwd();
|
|
20
|
+
const pulseConfigPath = `${currentDir}/pulse.config.ts`;
|
|
21
|
+
if (fs.existsSync(pulseConfigPath)) {
|
|
22
|
+
setIsInProjectDir(true);
|
|
23
|
+
}
|
|
24
|
+
}, []);
|
|
25
|
+
// Check if the user is authenticated
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
async function checkAuth() {
|
|
28
|
+
const token = getToken();
|
|
29
|
+
if (token) {
|
|
30
|
+
const isValid = await checkToken(token, cli.flags.dev);
|
|
31
|
+
if (isValid) {
|
|
32
|
+
setIsAuthenticated(true);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
setIsCheckingAuth(false);
|
|
36
|
+
}
|
|
37
|
+
if (isInProjectDir) {
|
|
38
|
+
checkAuth();
|
|
39
|
+
}
|
|
40
|
+
}, [isInProjectDir]);
|
|
41
|
+
// Build the extension
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
async function buildExtension() {
|
|
44
|
+
try {
|
|
45
|
+
setIsBuilding(true);
|
|
46
|
+
await $ `npm run build`;
|
|
47
|
+
// Zip the dist folder
|
|
48
|
+
await $({ cwd: 'dist' }) `zip -r ../node_modules/@pulse-editor/dist.zip *`;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
setIsBuildingError(true);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
setIsBuilding(false);
|
|
56
|
+
}
|
|
57
|
+
await publishExtension();
|
|
58
|
+
}
|
|
59
|
+
async function publishExtension() {
|
|
60
|
+
setIsPublishing(true);
|
|
61
|
+
// Upload the zip file to the server
|
|
62
|
+
try {
|
|
63
|
+
const formData = new FormData();
|
|
64
|
+
const buffer = fs.readFileSync('./node_modules/@pulse-editor/dist.zip');
|
|
65
|
+
const blob = new Blob([buffer], {
|
|
66
|
+
type: 'application/zip',
|
|
67
|
+
});
|
|
68
|
+
formData.append('file', blob, 'dist.zip');
|
|
69
|
+
// Send the file to the server
|
|
70
|
+
const res = await fetch(cli.flags.dev
|
|
71
|
+
? 'http://localhost:3000/api/extension/publish'
|
|
72
|
+
: 'https://pulse-editor.com/api/extension/publish', {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: {
|
|
75
|
+
Authorization: `Bearer ${getToken()}`,
|
|
76
|
+
},
|
|
77
|
+
body: formData,
|
|
78
|
+
});
|
|
79
|
+
if (res.status === 200) {
|
|
80
|
+
setIsPublished(true);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
setIsPublishingError(true);
|
|
84
|
+
const msg = await res.json();
|
|
85
|
+
if (msg.error) {
|
|
86
|
+
setFailureMessage(msg.error);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
setFailureMessage('Unknown error occurred while publishing.');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error('Error uploading the file:', error);
|
|
95
|
+
setIsPublishingError(true);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
setIsPublishing(false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (isAuthenticated) {
|
|
103
|
+
buildExtension();
|
|
104
|
+
}
|
|
105
|
+
}, [isAuthenticated]);
|
|
106
|
+
return (React.createElement(React.Fragment, null, !isInProjectDir ? (React.createElement(Text, { color: 'redBright' }, "\u26D4 The current directory does not contain a Pulse Editor project.")) : isCheckingAuth ? (React.createElement(Box, null,
|
|
107
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
108
|
+
React.createElement(Text, null, " Checking authentication..."))) : isAuthenticated ? (React.createElement(React.Fragment, null,
|
|
109
|
+
isBuilding && (React.createElement(Box, null,
|
|
110
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
111
|
+
React.createElement(Text, null, " Building..."))),
|
|
112
|
+
isBuildingError && (React.createElement(Text, { color: 'redBright' }, "\u274C Error building the extension. Please run `npm run build` to see the error.")),
|
|
113
|
+
isPublishing && (React.createElement(Box, null,
|
|
114
|
+
React.createElement(Spinner, { type: "dots" }),
|
|
115
|
+
React.createElement(Text, null, " Publishing..."))),
|
|
116
|
+
isPublishingError && (React.createElement(React.Fragment, null,
|
|
117
|
+
React.createElement(Text, { color: 'redBright' }, "\u274C Failed to publish extension."),
|
|
118
|
+
failureMessage && (React.createElement(Text, { color: 'redBright' },
|
|
119
|
+
"Error: ",
|
|
120
|
+
failureMessage)))),
|
|
121
|
+
isPublished && (React.createElement(Text, { color: 'greenBright' }, "\u2705 Extension published successfully.")))) : (React.createElement(Text, null,
|
|
122
|
+
"You are not authenticated or your access token is invalid. Publishing to Extension Marketplace is in Beta access. Please visit",
|
|
123
|
+
React.createElement(Text, { color: 'blueBright' }, " https://pulse-editor.com/beta "),
|
|
124
|
+
"to apply for Beta access."))));
|
|
125
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Box, Text } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export default function Header() {
|
|
4
|
+
return (React.createElement(Box, { flexDirection: "column", alignItems: "center" },
|
|
5
|
+
React.createElement(Text, { color: 'whiteBright' }, "Pulse Editor CLI"),
|
|
6
|
+
React.createElement(Text, null, "Version: 0.0.1"),
|
|
7
|
+
React.createElement(Text, { color: 'blueBright' }, "https://pulse-editor.com")));
|
|
8
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const flags: {
|
|
2
|
+
token: {
|
|
3
|
+
type: "boolean";
|
|
4
|
+
};
|
|
5
|
+
flow: {
|
|
6
|
+
type: "boolean";
|
|
7
|
+
};
|
|
8
|
+
framework: {
|
|
9
|
+
type: "string";
|
|
10
|
+
shortFlag: string;
|
|
11
|
+
};
|
|
12
|
+
dev: {
|
|
13
|
+
type: "boolean";
|
|
14
|
+
default: false;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type Flags = typeof flags;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Helper to preserve literal types *and* validate against AnyFlags
|
|
2
|
+
function defineFlags(flags) {
|
|
3
|
+
return flags;
|
|
4
|
+
}
|
|
5
|
+
export const flags = defineFlags({
|
|
6
|
+
token: {
|
|
7
|
+
type: 'boolean',
|
|
8
|
+
},
|
|
9
|
+
flow: {
|
|
10
|
+
type: 'boolean',
|
|
11
|
+
},
|
|
12
|
+
framework: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
shortFlag: 'f',
|
|
15
|
+
},
|
|
16
|
+
dev: {
|
|
17
|
+
type: 'boolean',
|
|
18
|
+
default: false,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const commandsManual: Record<string, string>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const help = `\
|
|
2
|
+
help [command] Show help for a command.
|
|
3
|
+
`;
|
|
4
|
+
const chat = `\
|
|
5
|
+
chat [message] (WIP) Chat with the Pulse Editor AI assistant.
|
|
6
|
+
|
|
7
|
+
Flags:
|
|
8
|
+
--interactive, -i
|
|
9
|
+
Start an interactive chat session
|
|
10
|
+
|
|
11
|
+
`;
|
|
12
|
+
const login = `\
|
|
13
|
+
login Login to the Pulse Editor Platform.
|
|
14
|
+
|
|
15
|
+
Flags:
|
|
16
|
+
--token [token]
|
|
17
|
+
Login using an access token. This is the default if the
|
|
18
|
+
token is set in the environment variable PE_ACCESS_TOKEN.
|
|
19
|
+
--flow
|
|
20
|
+
Login using a browser flow.
|
|
21
|
+
|
|
22
|
+
`;
|
|
23
|
+
const logout = `\
|
|
24
|
+
logout Logout from the Pulse Editor Platform.
|
|
25
|
+
`;
|
|
26
|
+
const publish = `\
|
|
27
|
+
publish Publish Pulse Editor Extension in current directory to the Pulse Editor Platform.
|
|
28
|
+
`;
|
|
29
|
+
const create = `\
|
|
30
|
+
create Create a new Pulse App using the starter template.
|
|
31
|
+
Flags:
|
|
32
|
+
--framework, -f [framework]
|
|
33
|
+
The framework to use for the new app.
|
|
34
|
+
Currently available options: react.
|
|
35
|
+
Future options: vue, angular, etc.
|
|
36
|
+
`;
|
|
37
|
+
export const commandsManual = {
|
|
38
|
+
help,
|
|
39
|
+
chat,
|
|
40
|
+
login,
|
|
41
|
+
logout,
|
|
42
|
+
publish,
|
|
43
|
+
create,
|
|
44
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
export function saveToken(token) {
|
|
5
|
+
// Save the token to .pulse-editor/config.json in user home directory
|
|
6
|
+
const configDir = path.join(os.homedir(), '.pulse-editor');
|
|
7
|
+
const configFile = path.join(configDir, 'config.json');
|
|
8
|
+
const config = {
|
|
9
|
+
accessToken: token,
|
|
10
|
+
};
|
|
11
|
+
if (!fs.existsSync(configDir)) {
|
|
12
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
15
|
+
}
|
|
16
|
+
export function getToken() {
|
|
17
|
+
// First try to get the token from the environment variable
|
|
18
|
+
const tokenEnv = process.env['PE_ACCESS_TOKEN'];
|
|
19
|
+
if (tokenEnv) {
|
|
20
|
+
return tokenEnv;
|
|
21
|
+
}
|
|
22
|
+
// If not found, try to get the token from the config file
|
|
23
|
+
const configDir = path.join(os.homedir(), '.pulse-editor');
|
|
24
|
+
const configFile = path.join(configDir, 'config.json');
|
|
25
|
+
if (fs.existsSync(configFile)) {
|
|
26
|
+
try {
|
|
27
|
+
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
28
|
+
if (config.accessToken) {
|
|
29
|
+
return config.accessToken;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error('Failed to parse config.json:', error);
|
|
34
|
+
// Return undefined if JSON parsing fails
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// If not found, return undefined
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
export function isTokenInEnv() {
|
|
42
|
+
// Check if the token is set in the environment variable
|
|
43
|
+
const tokenEnv = process.env['PE_ACCESS_TOKEN'];
|
|
44
|
+
if (tokenEnv) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
export async function checkToken(token, devMode) {
|
|
50
|
+
const res = await fetch(devMode
|
|
51
|
+
? 'http://localhost:3000/api/api-keys/check'
|
|
52
|
+
: 'https://pulse-editor.com/api/api-keys/check', {
|
|
53
|
+
body: JSON.stringify({ token }),
|
|
54
|
+
headers: {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
},
|
|
57
|
+
method: 'POST',
|
|
58
|
+
});
|
|
59
|
+
if (res.status === 200) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,25 +1,43 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
> This readme is automatically generated by [create-ink-app](https://github.com/vadimdemedes/create-ink-app)
|
|
4
|
-
|
|
1
|
+
# Pulse CLI
|
|
5
2
|
## Install
|
|
6
3
|
|
|
7
4
|
```bash
|
|
8
|
-
$ npm install --global cli
|
|
5
|
+
$ npm install --global @pulse-editor/cli
|
|
9
6
|
```
|
|
10
7
|
|
|
11
|
-
## CLI
|
|
8
|
+
## CLI Manual
|
|
12
9
|
|
|
13
10
|
```
|
|
14
|
-
$ cli --help
|
|
15
|
-
|
|
16
11
|
Usage
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
pulse [command] [flags]
|
|
13
|
+
|
|
14
|
+
Commands
|
|
15
|
+
help [command] Show help for a command.
|
|
16
|
+
chat [message] (WIP) Chat with the Pulse Editor AI assistant.
|
|
17
|
+
|
|
18
|
+
Flags:
|
|
19
|
+
--interactive, -i
|
|
20
|
+
Start an interactive chat session
|
|
21
|
+
|
|
22
|
+
login Login to the Pulse Editor Platform.
|
|
23
|
+
|
|
24
|
+
Flags:
|
|
25
|
+
--token [token]
|
|
26
|
+
Login using an access token. This is the default if the
|
|
27
|
+
token is set in the environment variable PE_ACCESS_TOKEN.
|
|
28
|
+
--flow
|
|
29
|
+
Login using a browser flow.
|
|
30
|
+
|
|
31
|
+
logout Logout from the Pulse Editor Platform.
|
|
32
|
+
publish Publish Pulse Editor Extension in current directory to the Pulse Editor Platform.
|
|
33
|
+
create Create a new Pulse App using the starter template.
|
|
34
|
+
Flags:
|
|
35
|
+
--framework, -f [framework]
|
|
36
|
+
The framework to use for the new app.
|
|
37
|
+
Currently available options: react.
|
|
38
|
+
Future options: vue, angular, etc.
|
|
21
39
|
|
|
22
40
|
Examples
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
pulse help publish
|
|
42
|
+
|
|
25
43
|
```
|