spck 0.3.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/.oxlintrc.json +49 -0
- package/LICENSE +21 -0
- package/README.md +631 -0
- package/bin/cli.js +20 -0
- package/bin/validate-cwd.js +41 -0
- package/dist/config/__tests__/config.test.d.ts +2 -0
- package/dist/config/__tests__/config.test.js +262 -0
- package/dist/config/__tests__/credentials.test.d.ts +2 -0
- package/dist/config/__tests__/credentials.test.js +360 -0
- package/dist/config/config.d.ts +33 -0
- package/dist/config/config.js +185 -0
- package/dist/config/credentials.d.ts +75 -0
- package/dist/config/credentials.js +259 -0
- package/dist/config/server-selection.d.ts +40 -0
- package/dist/config/server-selection.js +130 -0
- package/dist/connection/__tests__/firebase-auth.test.d.ts +2 -0
- package/dist/connection/__tests__/firebase-auth.test.js +96 -0
- package/dist/connection/__tests__/hmac.test.d.ts +2 -0
- package/dist/connection/__tests__/hmac.test.js +372 -0
- package/dist/connection/auth.d.ts +13 -0
- package/dist/connection/auth.js +91 -0
- package/dist/connection/firebase-auth.d.ts +40 -0
- package/dist/connection/firebase-auth.js +429 -0
- package/dist/connection/hmac.d.ts +24 -0
- package/dist/connection/hmac.js +109 -0
- package/dist/i18n/index.d.ts +25 -0
- package/dist/i18n/index.js +101 -0
- package/dist/i18n/locales/en.json +313 -0
- package/dist/i18n/locales/es.json +302 -0
- package/dist/i18n/locales/fr.json +302 -0
- package/dist/i18n/locales/id.json +302 -0
- package/dist/i18n/locales/ja.json +302 -0
- package/dist/i18n/locales/ko.json +302 -0
- package/dist/i18n/locales/locales/en.json +309 -0
- package/dist/i18n/locales/locales/es.json +302 -0
- package/dist/i18n/locales/locales/fr.json +302 -0
- package/dist/i18n/locales/locales/id.json +302 -0
- package/dist/i18n/locales/locales/ja.json +302 -0
- package/dist/i18n/locales/locales/ko.json +302 -0
- package/dist/i18n/locales/locales/pt.json +302 -0
- package/dist/i18n/locales/locales/zh-Hans.json +302 -0
- package/dist/i18n/locales/pt.json +302 -0
- package/dist/i18n/locales/zh-Hans.json +302 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +493 -0
- package/dist/proxy/ProxyClient.d.ts +125 -0
- package/dist/proxy/ProxyClient.js +781 -0
- package/dist/proxy/ProxySocketWrapper.d.ts +43 -0
- package/dist/proxy/ProxySocketWrapper.js +98 -0
- package/dist/proxy/__tests__/ProxyClient.test.d.ts +2 -0
- package/dist/proxy/__tests__/ProxyClient.test.js +445 -0
- package/dist/proxy/__tests__/ProxySocketWrapper.test.d.ts +2 -0
- package/dist/proxy/__tests__/ProxySocketWrapper.test.js +190 -0
- package/dist/proxy/__tests__/handshake-validation.test.d.ts +2 -0
- package/dist/proxy/__tests__/handshake-validation.test.js +282 -0
- package/dist/proxy/__tests__/token-refresh-race.test.d.ts +14 -0
- package/dist/proxy/__tests__/token-refresh-race.test.js +173 -0
- package/dist/proxy/chunking.d.ts +53 -0
- package/dist/proxy/chunking.js +127 -0
- package/dist/proxy/handshake-validation.d.ts +21 -0
- package/dist/proxy/handshake-validation.js +49 -0
- package/dist/rpc/__tests__/router.test.d.ts +2 -0
- package/dist/rpc/__tests__/router.test.js +262 -0
- package/dist/rpc/router.d.ts +37 -0
- package/dist/rpc/router.js +132 -0
- package/dist/services/BrowserProxyService.d.ts +13 -0
- package/dist/services/BrowserProxyService.js +139 -0
- package/dist/services/FilesystemService.d.ts +99 -0
- package/dist/services/FilesystemService.js +742 -0
- package/dist/services/GitService.d.ts +243 -0
- package/dist/services/GitService.js +1439 -0
- package/dist/services/SearchService.d.ts +93 -0
- package/dist/services/SearchService.js +670 -0
- package/dist/services/TerminalService.d.ts +62 -0
- package/dist/services/TerminalService.js +337 -0
- package/dist/services/__tests__/BrowserProxyService.test.d.ts +2 -0
- package/dist/services/__tests__/BrowserProxyService.test.js +145 -0
- package/dist/services/__tests__/FilesystemService.test.d.ts +2 -0
- package/dist/services/__tests__/FilesystemService.test.js +609 -0
- package/dist/services/__tests__/GitService.test.d.ts +2 -0
- package/dist/services/__tests__/GitService.test.js +953 -0
- package/dist/services/__tests__/SearchService.test.d.ts +2 -0
- package/dist/services/__tests__/SearchService.test.js +384 -0
- package/dist/services/__tests__/TerminalService.test.d.ts +2 -0
- package/dist/services/__tests__/TerminalService.test.js +513 -0
- package/dist/setup/wizard.d.ts +10 -0
- package/dist/setup/wizard.js +172 -0
- package/dist/types.d.ts +196 -0
- package/dist/types.js +44 -0
- package/dist/utils/__tests__/gitignore.test.d.ts +2 -0
- package/dist/utils/__tests__/gitignore.test.js +127 -0
- package/dist/utils/gitignore.d.ts +24 -0
- package/dist/utils/gitignore.js +77 -0
- package/dist/utils/logger.d.ts +96 -0
- package/dist/utils/logger.js +456 -0
- package/dist/utils/project-dir.d.ts +51 -0
- package/dist/utils/project-dir.js +191 -0
- package/dist/utils/ripgrep.d.ts +34 -0
- package/dist/utils/ripgrep.js +148 -0
- package/dist/utils/tool-detection.d.ts +17 -0
- package/dist/utils/tool-detection.js +126 -0
- package/dist/watcher/FileWatcher.d.ts +10 -0
- package/dist/watcher/FileWatcher.js +42 -0
- package/package.json +70 -0
- package/src/config/__tests__/config.test.ts +318 -0
- package/src/config/__tests__/credentials.test.ts +494 -0
- package/src/config/config.ts +206 -0
- package/src/config/credentials.ts +302 -0
- package/src/config/server-selection.ts +150 -0
- package/src/connection/__tests__/firebase-auth.test.ts +121 -0
- package/src/connection/__tests__/hmac.test.ts +509 -0
- package/src/connection/auth.ts +140 -0
- package/src/connection/firebase-auth.ts +504 -0
- package/src/connection/hmac.ts +139 -0
- package/src/i18n/index.ts +119 -0
- package/src/i18n/locales/en.json +313 -0
- package/src/i18n/locales/es.json +302 -0
- package/src/i18n/locales/fr.json +302 -0
- package/src/i18n/locales/id.json +302 -0
- package/src/i18n/locales/ja.json +302 -0
- package/src/i18n/locales/ko.json +302 -0
- package/src/i18n/locales/pt.json +302 -0
- package/src/i18n/locales/zh-Hans.json +302 -0
- package/src/index.ts +542 -0
- package/src/proxy/ProxyClient.ts +968 -0
- package/src/proxy/ProxySocketWrapper.ts +113 -0
- package/src/proxy/__tests__/ProxyClient.test.ts +575 -0
- package/src/proxy/__tests__/ProxySocketWrapper.test.ts +251 -0
- package/src/proxy/__tests__/handshake-validation.test.ts +367 -0
- package/src/proxy/chunking.ts +162 -0
- package/src/proxy/handshake-validation.ts +64 -0
- package/src/rpc/__tests__/router.test.ts +400 -0
- package/src/rpc/router.ts +183 -0
- package/src/services/BrowserProxyService.ts +179 -0
- package/src/services/FilesystemService.ts +841 -0
- package/src/services/GitService.ts +1639 -0
- package/src/services/SearchService.ts +809 -0
- package/src/services/TerminalService.ts +413 -0
- package/src/services/__tests__/BrowserProxyService.test.ts +155 -0
- package/src/services/__tests__/FilesystemService.test.ts +1002 -0
- package/src/services/__tests__/GitService.test.ts +1552 -0
- package/src/services/__tests__/SearchService.test.ts +484 -0
- package/src/services/__tests__/TerminalService.test.ts +702 -0
- package/src/setup/wizard.ts +242 -0
- package/src/types/fossil-delta.d.ts +4 -0
- package/src/types.ts +287 -0
- package/src/utils/__tests__/gitignore.test.ts +174 -0
- package/src/utils/gitignore.ts +91 -0
- package/src/utils/logger.ts +578 -0
- package/src/utils/project-dir.ts +218 -0
- package/src/utils/ripgrep.ts +180 -0
- package/src/utils/tool-detection.ts +141 -0
- package/src/watcher/FileWatcher.ts +53 -0
- package/tsconfig.json +24 -0
- package/vitest.config.ts +19 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive setup wizard for spck-cli
|
|
3
|
+
* Configures CLI to connect to proxy server
|
|
4
|
+
*/
|
|
5
|
+
import * as readline from 'readline';
|
|
6
|
+
import { saveConfig, createDefaultConfig } from '../config/config.js';
|
|
7
|
+
import { ensureProjectDir } from '../utils/project-dir.js';
|
|
8
|
+
import { gitignoreExists, isSpckEditorIgnored, addSpckEditorToGitignore } from '../utils/gitignore.js';
|
|
9
|
+
import { t } from '../i18n/index.js';
|
|
10
|
+
const USER_AUTH_DOCS_URL = 'https://spck.io/docs/cli#user-authentication';
|
|
11
|
+
/**
|
|
12
|
+
* Create readline interface
|
|
13
|
+
*/
|
|
14
|
+
function createPrompt() {
|
|
15
|
+
return readline.createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Prompt user for input
|
|
22
|
+
*/
|
|
23
|
+
function question(rl, query) {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
rl.question(query, (answer) => {
|
|
26
|
+
resolve(answer);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Ask yes/no question
|
|
32
|
+
*/
|
|
33
|
+
async function questionYesNo(rl, query, defaultValue) {
|
|
34
|
+
const answer = await question(rl, query);
|
|
35
|
+
const normalized = answer.trim().toLowerCase();
|
|
36
|
+
if (normalized === '') {
|
|
37
|
+
return defaultValue;
|
|
38
|
+
}
|
|
39
|
+
return normalized === 'y' || normalized === 'yes';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run the setup wizard
|
|
43
|
+
*/
|
|
44
|
+
export async function runSetup(configPath) {
|
|
45
|
+
const rl = createPrompt();
|
|
46
|
+
console.log('\n' + '='.repeat(60));
|
|
47
|
+
console.log(' ' + t('setup.title'));
|
|
48
|
+
console.log('='.repeat(60) + '\n');
|
|
49
|
+
console.log(t('setup.description1'));
|
|
50
|
+
console.log(t('setup.description2'));
|
|
51
|
+
console.log(t('setup.description3') + '\n');
|
|
52
|
+
try {
|
|
53
|
+
// Step 1: Root directory
|
|
54
|
+
console.log('--- ' + t('setup.projectConfig') + ' ---\n');
|
|
55
|
+
const root = await question(rl, t('setup.rootDirPrompt', { default: process.cwd() }));
|
|
56
|
+
const rootPath = root.trim() || process.cwd();
|
|
57
|
+
// Step 1.5: Server name (for QR code identification)
|
|
58
|
+
const defaultConfig = createDefaultConfig();
|
|
59
|
+
// Step 2: Terminal service
|
|
60
|
+
console.log('\n--- ' + t('setup.terminalConfig') + ' ---\n');
|
|
61
|
+
console.log(t('setup.terminalDescription'));
|
|
62
|
+
const terminalEnabled = await questionYesNo(rl, t('setup.terminalPrompt'), true);
|
|
63
|
+
let maxBufferedLines = 5000;
|
|
64
|
+
let maxTerminals = 10;
|
|
65
|
+
// Step 4: Advanced terminal configuration
|
|
66
|
+
if (terminalEnabled) {
|
|
67
|
+
const advancedTerminal = await questionYesNo(rl, '\n' + t('setup.advancedTerminalPrompt'), false);
|
|
68
|
+
if (advancedTerminal) {
|
|
69
|
+
console.log('');
|
|
70
|
+
const bufferInput = await question(rl, t('setup.maxBufferPrompt', { default: String(maxBufferedLines) }));
|
|
71
|
+
maxBufferedLines = parseInt(bufferInput) || 5000;
|
|
72
|
+
console.log(' ' + t('setup.maxBufferHint') + '\n');
|
|
73
|
+
const maxTermInput = await question(rl, t('setup.maxTerminalsPrompt', { default: String(maxTerminals) }));
|
|
74
|
+
maxTerminals = parseInt(maxTermInput) || 10;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Step 5: Browser proxy configuration
|
|
78
|
+
console.log('\n--- ' + t('setup.browserProxyConfig') + ' ---\n');
|
|
79
|
+
console.log(t('setup.browserProxyDescription') + '\n');
|
|
80
|
+
const browserProxyEnabled = await questionYesNo(rl, t('setup.browserProxyPrompt'), true);
|
|
81
|
+
// Step 6: Security configuration
|
|
82
|
+
console.log('\n--- ' + t('setup.securityConfig') + ' ---\n');
|
|
83
|
+
console.log(t('setup.securityDescription1'));
|
|
84
|
+
console.log(t('setup.securityDescription2'));
|
|
85
|
+
console.log(t('setup.securityDescription3'));
|
|
86
|
+
console.log(t('setup.securityDocsHint', { url: USER_AUTH_DOCS_URL }) + '\n');
|
|
87
|
+
const userAuthEnabled = await questionYesNo(rl, t('setup.securityPrompt'), false);
|
|
88
|
+
// Step 6: .gitignore configuration (advanced)
|
|
89
|
+
let shouldAddToGitignore = false;
|
|
90
|
+
if (gitignoreExists(rootPath)) {
|
|
91
|
+
if (!isSpckEditorIgnored(rootPath)) {
|
|
92
|
+
console.log('\n--- ' + t('setup.gitConfig') + ' ---\n');
|
|
93
|
+
console.log(t('setup.gitignoreDetected'));
|
|
94
|
+
console.log(t('setup.gitignoreRecommend1'));
|
|
95
|
+
console.log(t('setup.gitignoreRecommend2') + '\n');
|
|
96
|
+
shouldAddToGitignore = await questionYesNo(rl, t('setup.gitignorePrompt'), true);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
rl.close();
|
|
100
|
+
// Create configuration
|
|
101
|
+
const config = {
|
|
102
|
+
version: 1,
|
|
103
|
+
root: rootPath,
|
|
104
|
+
name: defaultConfig.name,
|
|
105
|
+
terminal: {
|
|
106
|
+
enabled: terminalEnabled,
|
|
107
|
+
maxBufferedLines,
|
|
108
|
+
maxTerminals
|
|
109
|
+
},
|
|
110
|
+
security: {
|
|
111
|
+
userAuthenticationEnabled: userAuthEnabled
|
|
112
|
+
},
|
|
113
|
+
browserProxy: {
|
|
114
|
+
enabled: browserProxyEnabled
|
|
115
|
+
},
|
|
116
|
+
filesystem: {
|
|
117
|
+
maxFileSize: '10MB',
|
|
118
|
+
watchIgnorePatterns: [
|
|
119
|
+
'**/.git/**',
|
|
120
|
+
'**/.spck-editor/**',
|
|
121
|
+
'**/node_modules/**',
|
|
122
|
+
'**/*.log',
|
|
123
|
+
'**/.DS_Store',
|
|
124
|
+
'**/dist/**',
|
|
125
|
+
'**/build/**'
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
// Ensure project directory exists (creates symlink)
|
|
130
|
+
ensureProjectDir(config.root);
|
|
131
|
+
// Save configuration
|
|
132
|
+
saveConfig(config, configPath);
|
|
133
|
+
// Add to .gitignore if requested
|
|
134
|
+
if (shouldAddToGitignore) {
|
|
135
|
+
try {
|
|
136
|
+
addSpckEditorToGitignore(config.root);
|
|
137
|
+
console.log('\n✅ ' + t('setup.gitignoreAdded'));
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.warn('\n⚠️ ' + t('setup.gitignoreFailed', { message: error.message }));
|
|
141
|
+
console.warn(' ' + t('setup.gitignoreManualHint'));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
console.log('\n' + '='.repeat(60));
|
|
145
|
+
console.log('✅ ' + t('config.saved'));
|
|
146
|
+
console.log('='.repeat(60) + '\n');
|
|
147
|
+
displayConfigSummary(config);
|
|
148
|
+
return config;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
rl.close();
|
|
152
|
+
console.error('\n❌ ' + t('setup.setupFailed', { message: error.message }));
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Display configuration summary
|
|
158
|
+
*/
|
|
159
|
+
function displayConfigSummary(config) {
|
|
160
|
+
console.log(t('setup.configSummary'));
|
|
161
|
+
console.log(' ' + t('setup.summaryName', { name: config.name || t('setup.summaryNameNotSet') }));
|
|
162
|
+
console.log(' ' + t('setup.summaryRoot', { root: config.root }));
|
|
163
|
+
console.log(' ' + t('setup.summaryTerminal', { status: config.terminal.enabled ? t('setup.summaryEnabled') : t('setup.summaryDisabled') }));
|
|
164
|
+
if (config.terminal.enabled) {
|
|
165
|
+
console.log(' ' + t('setup.summaryMaxBuffer', { value: String(config.terminal.maxBufferedLines) }));
|
|
166
|
+
console.log(' ' + t('setup.summaryMaxProcesses', { value: String(config.terminal.maxTerminals) }));
|
|
167
|
+
}
|
|
168
|
+
console.log(' ' + t('setup.summaryUserAuth', { status: config.security.userAuthenticationEnabled ? t('setup.summaryEnabled') : t('setup.summaryDisabled') }));
|
|
169
|
+
console.log(' ' + t('setup.summaryBrowserProxy', { status: config.browserProxy?.enabled !== false ? t('setup.summaryEnabled') : t('setup.summaryDisabled') }));
|
|
170
|
+
console.log('');
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=wizard.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for spck-cli server
|
|
3
|
+
*/
|
|
4
|
+
export interface SocketInterface {
|
|
5
|
+
id: string;
|
|
6
|
+
emit(event: string, data?: any): boolean;
|
|
7
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
8
|
+
off(event: string, listener: (...args: any[]) => void): this;
|
|
9
|
+
broadcast: {
|
|
10
|
+
emit(event: string, data?: any): boolean;
|
|
11
|
+
};
|
|
12
|
+
data: {
|
|
13
|
+
uid: string;
|
|
14
|
+
deviceId: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface ServerConfig {
|
|
18
|
+
version: number;
|
|
19
|
+
root: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
terminal: {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
maxBufferedLines: number;
|
|
24
|
+
maxTerminals: number;
|
|
25
|
+
};
|
|
26
|
+
security: {
|
|
27
|
+
userAuthenticationEnabled: boolean;
|
|
28
|
+
};
|
|
29
|
+
filesystem: {
|
|
30
|
+
maxFileSize: string;
|
|
31
|
+
watchIgnorePatterns: string[];
|
|
32
|
+
};
|
|
33
|
+
browserProxy?: {
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface ConnectionSettings {
|
|
38
|
+
serverToken: string;
|
|
39
|
+
serverTokenExpiry: number;
|
|
40
|
+
clientId: string;
|
|
41
|
+
secret: string;
|
|
42
|
+
userId: string;
|
|
43
|
+
connectedAt: number;
|
|
44
|
+
}
|
|
45
|
+
export interface StoredCredentials {
|
|
46
|
+
refreshToken: string;
|
|
47
|
+
userId: string;
|
|
48
|
+
proxyServerUrl?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface GlobalConfig {
|
|
51
|
+
knownDeviceIds: string[];
|
|
52
|
+
}
|
|
53
|
+
export interface FirebaseCredentials {
|
|
54
|
+
firebaseToken: string;
|
|
55
|
+
firebaseTokenExpiry: number;
|
|
56
|
+
refreshToken: string;
|
|
57
|
+
userId: string;
|
|
58
|
+
}
|
|
59
|
+
export interface JSONRPCRequest {
|
|
60
|
+
jsonrpc: '2.0';
|
|
61
|
+
method: string;
|
|
62
|
+
params?: any;
|
|
63
|
+
id?: number | string;
|
|
64
|
+
timestamp?: number;
|
|
65
|
+
hmac: string;
|
|
66
|
+
nonce: string;
|
|
67
|
+
}
|
|
68
|
+
export interface JSONRPCResponse {
|
|
69
|
+
jsonrpc: '2.0';
|
|
70
|
+
result?: any;
|
|
71
|
+
error?: JSONRPCError;
|
|
72
|
+
id: number | string | null;
|
|
73
|
+
}
|
|
74
|
+
export interface JSONRPCError {
|
|
75
|
+
code: number;
|
|
76
|
+
message: string;
|
|
77
|
+
data?: any;
|
|
78
|
+
}
|
|
79
|
+
export interface JSONRPCNotification {
|
|
80
|
+
jsonrpc: '2.0';
|
|
81
|
+
method: string;
|
|
82
|
+
params?: any;
|
|
83
|
+
}
|
|
84
|
+
export interface JWTPayload {
|
|
85
|
+
aud: string;
|
|
86
|
+
iat: number;
|
|
87
|
+
exp: number;
|
|
88
|
+
iss: string;
|
|
89
|
+
sub: string;
|
|
90
|
+
[key: string]: any;
|
|
91
|
+
}
|
|
92
|
+
export interface AuthenticatedSocket extends SocketInterface {
|
|
93
|
+
}
|
|
94
|
+
export declare enum ErrorCode {
|
|
95
|
+
PARSE_ERROR = -32700,
|
|
96
|
+
INVALID_REQUEST = -32600,
|
|
97
|
+
METHOD_NOT_FOUND = -32601,
|
|
98
|
+
INVALID_PARAMS = -32602,
|
|
99
|
+
INTERNAL_ERROR = -32603,
|
|
100
|
+
AUTHENTICATION_FAILED = -32001,
|
|
101
|
+
JWT_EXPIRED = -32002,
|
|
102
|
+
HMAC_VALIDATION_FAILED = -32003,
|
|
103
|
+
PERMISSION_DENIED = -32005,
|
|
104
|
+
FILE_NOT_FOUND = -32004,
|
|
105
|
+
WRITE_CONFLICT = -32006,
|
|
106
|
+
INVALID_PATH = -32007,
|
|
107
|
+
FILE_TOO_LARGE = -32031,
|
|
108
|
+
INVALID_ENCODING = -32032,
|
|
109
|
+
DELTA_PATCH_FAILED = -32033,
|
|
110
|
+
GIT_OPERATION_FAILED = -32010,
|
|
111
|
+
INVALID_OID = -32011,
|
|
112
|
+
REPOSITORY_NOT_FOUND = -32012,
|
|
113
|
+
TERMINAL_NOT_FOUND = -32020,
|
|
114
|
+
TERMINAL_LIMIT_EXCEEDED = -32021,
|
|
115
|
+
TERMINAL_PROCESS_EXITED = -32022,
|
|
116
|
+
BROWSER_PROXY_REQUEST_FAILED = -32050,
|
|
117
|
+
OPERATION_TIMEOUT = -32030,
|
|
118
|
+
UID_NOT_AUTHORIZED = -32040,
|
|
119
|
+
FEATURE_DISABLED = -32041
|
|
120
|
+
}
|
|
121
|
+
export declare function createRPCError(code: ErrorCode, message: string, data?: any): JSONRPCError;
|
|
122
|
+
export type HandshakeMessageType = 'auth' | 'auth_result' | 'request_user_verification' | 'user_verification' | 'protocol_info' | 'protocol_selected' | 'connected';
|
|
123
|
+
export interface ClientAuthMessage {
|
|
124
|
+
type: 'auth';
|
|
125
|
+
jwt: string;
|
|
126
|
+
}
|
|
127
|
+
export interface AuthResultMessage {
|
|
128
|
+
type: 'auth_result';
|
|
129
|
+
success: boolean;
|
|
130
|
+
error?: string;
|
|
131
|
+
}
|
|
132
|
+
export interface UserVerificationRequestMessage {
|
|
133
|
+
type: 'request_user_verification';
|
|
134
|
+
message: string;
|
|
135
|
+
}
|
|
136
|
+
export interface UserVerificationMessage {
|
|
137
|
+
type: 'user_verification';
|
|
138
|
+
firebaseToken: string;
|
|
139
|
+
}
|
|
140
|
+
export interface ProtocolInfoMessage {
|
|
141
|
+
type: 'protocol_info';
|
|
142
|
+
minVersion: number;
|
|
143
|
+
maxVersion: number;
|
|
144
|
+
features: {
|
|
145
|
+
terminal: boolean;
|
|
146
|
+
git: boolean;
|
|
147
|
+
fastSearch: boolean;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
export interface ProtocolSelectedMessage {
|
|
151
|
+
type: 'protocol_selected';
|
|
152
|
+
version: number;
|
|
153
|
+
ready: boolean;
|
|
154
|
+
}
|
|
155
|
+
export interface ConnectedMessage {
|
|
156
|
+
type: 'connected';
|
|
157
|
+
message: string;
|
|
158
|
+
}
|
|
159
|
+
export type HandshakeMessage = ClientAuthMessage | AuthResultMessage | UserVerificationRequestMessage | UserVerificationMessage | ProtocolInfoMessage | ProtocolSelectedMessage | ConnectedMessage;
|
|
160
|
+
export interface FreeTierInfo {
|
|
161
|
+
dailyLimitSeconds: number;
|
|
162
|
+
usedSeconds: number;
|
|
163
|
+
}
|
|
164
|
+
export interface ProxyAuthenticatedEvent {
|
|
165
|
+
token: string;
|
|
166
|
+
clientId: string;
|
|
167
|
+
userId: string;
|
|
168
|
+
expiresAt: number;
|
|
169
|
+
freeTierInfo?: FreeTierInfo | null;
|
|
170
|
+
}
|
|
171
|
+
export interface ProxyClientConnectingEvent {
|
|
172
|
+
connectionId: string;
|
|
173
|
+
}
|
|
174
|
+
export interface ProxyMultipleConnectionEvent {
|
|
175
|
+
existingConnections: string[];
|
|
176
|
+
newConnectionId: string;
|
|
177
|
+
}
|
|
178
|
+
export interface ProxyClientMessageEvent {
|
|
179
|
+
connectionId: string;
|
|
180
|
+
data: any;
|
|
181
|
+
}
|
|
182
|
+
export interface ProxyClientDisconnectedEvent {
|
|
183
|
+
connectionId: string;
|
|
184
|
+
reason?: string;
|
|
185
|
+
resetTime?: number;
|
|
186
|
+
}
|
|
187
|
+
export interface ProxyErrorEvent {
|
|
188
|
+
code: string;
|
|
189
|
+
message: string;
|
|
190
|
+
[key: string]: any;
|
|
191
|
+
}
|
|
192
|
+
export interface ToolDetectionResult {
|
|
193
|
+
git: boolean;
|
|
194
|
+
ripgrep: boolean;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for spck-cli server
|
|
3
|
+
*/
|
|
4
|
+
// Error codes (JSON-RPC 2.0 + custom)
|
|
5
|
+
export var ErrorCode;
|
|
6
|
+
(function (ErrorCode) {
|
|
7
|
+
// Standard JSON-RPC 2.0
|
|
8
|
+
ErrorCode[ErrorCode["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
|
|
9
|
+
ErrorCode[ErrorCode["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST";
|
|
10
|
+
ErrorCode[ErrorCode["METHOD_NOT_FOUND"] = -32601] = "METHOD_NOT_FOUND";
|
|
11
|
+
ErrorCode[ErrorCode["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
|
|
12
|
+
ErrorCode[ErrorCode["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR";
|
|
13
|
+
// Authentication & Security
|
|
14
|
+
ErrorCode[ErrorCode["AUTHENTICATION_FAILED"] = -32001] = "AUTHENTICATION_FAILED";
|
|
15
|
+
ErrorCode[ErrorCode["JWT_EXPIRED"] = -32002] = "JWT_EXPIRED";
|
|
16
|
+
ErrorCode[ErrorCode["HMAC_VALIDATION_FAILED"] = -32003] = "HMAC_VALIDATION_FAILED";
|
|
17
|
+
ErrorCode[ErrorCode["PERMISSION_DENIED"] = -32005] = "PERMISSION_DENIED";
|
|
18
|
+
// Filesystem
|
|
19
|
+
ErrorCode[ErrorCode["FILE_NOT_FOUND"] = -32004] = "FILE_NOT_FOUND";
|
|
20
|
+
ErrorCode[ErrorCode["WRITE_CONFLICT"] = -32006] = "WRITE_CONFLICT";
|
|
21
|
+
ErrorCode[ErrorCode["INVALID_PATH"] = -32007] = "INVALID_PATH";
|
|
22
|
+
ErrorCode[ErrorCode["FILE_TOO_LARGE"] = -32031] = "FILE_TOO_LARGE";
|
|
23
|
+
ErrorCode[ErrorCode["INVALID_ENCODING"] = -32032] = "INVALID_ENCODING";
|
|
24
|
+
ErrorCode[ErrorCode["DELTA_PATCH_FAILED"] = -32033] = "DELTA_PATCH_FAILED";
|
|
25
|
+
// Git
|
|
26
|
+
ErrorCode[ErrorCode["GIT_OPERATION_FAILED"] = -32010] = "GIT_OPERATION_FAILED";
|
|
27
|
+
ErrorCode[ErrorCode["INVALID_OID"] = -32011] = "INVALID_OID";
|
|
28
|
+
ErrorCode[ErrorCode["REPOSITORY_NOT_FOUND"] = -32012] = "REPOSITORY_NOT_FOUND";
|
|
29
|
+
// Terminal
|
|
30
|
+
ErrorCode[ErrorCode["TERMINAL_NOT_FOUND"] = -32020] = "TERMINAL_NOT_FOUND";
|
|
31
|
+
ErrorCode[ErrorCode["TERMINAL_LIMIT_EXCEEDED"] = -32021] = "TERMINAL_LIMIT_EXCEEDED";
|
|
32
|
+
ErrorCode[ErrorCode["TERMINAL_PROCESS_EXITED"] = -32022] = "TERMINAL_PROCESS_EXITED";
|
|
33
|
+
// Browser Proxy
|
|
34
|
+
ErrorCode[ErrorCode["BROWSER_PROXY_REQUEST_FAILED"] = -32050] = "BROWSER_PROXY_REQUEST_FAILED";
|
|
35
|
+
// General
|
|
36
|
+
ErrorCode[ErrorCode["OPERATION_TIMEOUT"] = -32030] = "OPERATION_TIMEOUT";
|
|
37
|
+
ErrorCode[ErrorCode["UID_NOT_AUTHORIZED"] = -32040] = "UID_NOT_AUTHORIZED";
|
|
38
|
+
ErrorCode[ErrorCode["FEATURE_DISABLED"] = -32041] = "FEATURE_DISABLED";
|
|
39
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
40
|
+
// Helper to create JSON-RPC error
|
|
41
|
+
export function createRPCError(code, message, data) {
|
|
42
|
+
return { code, message, data };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* Tests for .gitignore utilities
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import { gitignoreExists, isSpckEditorIgnored, addSpckEditorToGitignore, getGitignorePath, } from '../gitignore';
|
|
9
|
+
describe('gitignore utilities', () => {
|
|
10
|
+
let tempDir;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
// Create a temporary directory for each test
|
|
13
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'spck-gitignore-test-'));
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
// Clean up temporary directory
|
|
17
|
+
if (fs.existsSync(tempDir)) {
|
|
18
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
describe('gitignoreExists', () => {
|
|
22
|
+
it('should return false when .gitignore does not exist', () => {
|
|
23
|
+
expect(gitignoreExists(tempDir)).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
it('should return true when .gitignore exists', () => {
|
|
26
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
27
|
+
fs.writeFileSync(gitignorePath, 'node_modules/\n', 'utf8');
|
|
28
|
+
expect(gitignoreExists(tempDir)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('isSpckEditorIgnored', () => {
|
|
32
|
+
it('should return false when .gitignore does not exist', () => {
|
|
33
|
+
expect(isSpckEditorIgnored(tempDir)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
it('should return false when .spck-editor/ is not in .gitignore', () => {
|
|
36
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
37
|
+
fs.writeFileSync(gitignorePath, 'node_modules/\ndist/\n', 'utf8');
|
|
38
|
+
expect(isSpckEditorIgnored(tempDir)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it('should return true when .spck-editor/ is in .gitignore', () => {
|
|
41
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
42
|
+
fs.writeFileSync(gitignorePath, 'node_modules/\n.spck-editor/\ndist/\n', 'utf8');
|
|
43
|
+
expect(isSpckEditorIgnored(tempDir)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
it('should return false when only .spck-editor (without slash) is in .gitignore', () => {
|
|
46
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
47
|
+
fs.writeFileSync(gitignorePath, 'node_modules/\n.spck-editor\ndist/\n', 'utf8');
|
|
48
|
+
expect(isSpckEditorIgnored(tempDir)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
it('should ignore comments in .gitignore', () => {
|
|
51
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
52
|
+
fs.writeFileSync(gitignorePath, '# This is a comment\nnode_modules/\n# .spck-editor/\ndist/\n', 'utf8');
|
|
53
|
+
expect(isSpckEditorIgnored(tempDir)).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('should ignore empty lines in .gitignore', () => {
|
|
56
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
57
|
+
fs.writeFileSync(gitignorePath, 'node_modules/\n\n\ndist/\n', 'utf8');
|
|
58
|
+
expect(isSpckEditorIgnored(tempDir)).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
it('should handle .gitignore with only whitespace lines', () => {
|
|
61
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
62
|
+
fs.writeFileSync(gitignorePath, ' \n\t\n\n', 'utf8');
|
|
63
|
+
expect(isSpckEditorIgnored(tempDir)).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('addSpckEditorToGitignore', () => {
|
|
67
|
+
it('should create .gitignore with .spck-editor/ when file does not exist', () => {
|
|
68
|
+
addSpckEditorToGitignore(tempDir);
|
|
69
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
70
|
+
expect(fs.existsSync(gitignorePath)).toBe(true);
|
|
71
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
72
|
+
expect(content).toContain('.spck-editor/');
|
|
73
|
+
expect(content).toContain('# Spck CLI project data');
|
|
74
|
+
});
|
|
75
|
+
it('should append .spck-editor/ to existing .gitignore', () => {
|
|
76
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
77
|
+
fs.writeFileSync(gitignorePath, 'node_modules/\ndist/\n', 'utf8');
|
|
78
|
+
addSpckEditorToGitignore(tempDir);
|
|
79
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
80
|
+
expect(content).toContain('node_modules/');
|
|
81
|
+
expect(content).toContain('dist/');
|
|
82
|
+
expect(content).toContain('.spck-editor/');
|
|
83
|
+
expect(content).toContain('# Spck CLI project data');
|
|
84
|
+
});
|
|
85
|
+
it('should add newline before comment if .gitignore does not end with newline', () => {
|
|
86
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
87
|
+
fs.writeFileSync(gitignorePath, 'node_modules/', 'utf8'); // No trailing newline
|
|
88
|
+
addSpckEditorToGitignore(tempDir);
|
|
89
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
90
|
+
expect(content).toBe('node_modules/\n\n# Spck CLI project data\n.spck-editor/\n');
|
|
91
|
+
});
|
|
92
|
+
it('should not add .spck-editor/ if already present', () => {
|
|
93
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
94
|
+
const initialContent = 'node_modules/\n.spck-editor/\ndist/\n';
|
|
95
|
+
fs.writeFileSync(gitignorePath, initialContent, 'utf8');
|
|
96
|
+
addSpckEditorToGitignore(tempDir);
|
|
97
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
98
|
+
// Content should be unchanged
|
|
99
|
+
expect(content).toBe(initialContent);
|
|
100
|
+
});
|
|
101
|
+
it('should add .spck-editor/ even if .spck-editor (without slash) is present', () => {
|
|
102
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
103
|
+
const initialContent = 'node_modules/\n.spck-editor\ndist/\n';
|
|
104
|
+
fs.writeFileSync(gitignorePath, initialContent, 'utf8');
|
|
105
|
+
addSpckEditorToGitignore(tempDir);
|
|
106
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
107
|
+
// Should add the pattern since .spck-editor (without slash) is different from .spck-editor/
|
|
108
|
+
expect(content).toContain('.spck-editor/');
|
|
109
|
+
expect(content).toContain('# Spck CLI project data');
|
|
110
|
+
});
|
|
111
|
+
it('should handle empty .gitignore file', () => {
|
|
112
|
+
const gitignorePath = path.join(tempDir, '.gitignore');
|
|
113
|
+
fs.writeFileSync(gitignorePath, '', 'utf8');
|
|
114
|
+
addSpckEditorToGitignore(tempDir);
|
|
115
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
116
|
+
expect(content).toContain('.spck-editor/');
|
|
117
|
+
expect(content).toContain('# Spck CLI project data');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('getGitignorePath', () => {
|
|
121
|
+
it('should return correct .gitignore path', () => {
|
|
122
|
+
const expected = path.join(tempDir, '.gitignore');
|
|
123
|
+
expect(getGitignorePath(tempDir)).toBe(expected);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=gitignore.test.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .gitignore file management utilities
|
|
3
|
+
* Handles checking and updating .gitignore files
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Check if .gitignore exists in a directory
|
|
7
|
+
*/
|
|
8
|
+
export declare function gitignoreExists(directory: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Check if .gitignore contains the .spck-editor pattern
|
|
11
|
+
* Returns true if the pattern is found (exact match)
|
|
12
|
+
*/
|
|
13
|
+
export declare function isSpckEditorIgnored(directory: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Add .spck-editor/ to .gitignore
|
|
16
|
+
* Creates .gitignore if it doesn't exist
|
|
17
|
+
* Appends the pattern if not already present
|
|
18
|
+
*/
|
|
19
|
+
export declare function addSpckEditorToGitignore(directory: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get the full path to .gitignore in a directory
|
|
22
|
+
*/
|
|
23
|
+
export declare function getGitignorePath(directory: string): string;
|
|
24
|
+
//# sourceMappingURL=gitignore.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .gitignore file management utilities
|
|
3
|
+
* Handles checking and updating .gitignore files
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
const DIR_PATTERN = '.spck-editor/';
|
|
8
|
+
/**
|
|
9
|
+
* Check if .gitignore exists in a directory
|
|
10
|
+
*/
|
|
11
|
+
export function gitignoreExists(directory) {
|
|
12
|
+
const gitignorePath = path.join(directory, '.gitignore');
|
|
13
|
+
return fs.existsSync(gitignorePath);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if .gitignore contains the .spck-editor pattern
|
|
17
|
+
* Returns true if the pattern is found (exact match)
|
|
18
|
+
*/
|
|
19
|
+
export function isSpckEditorIgnored(directory) {
|
|
20
|
+
const gitignorePath = path.join(directory, '.gitignore');
|
|
21
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
26
|
+
const lines = content.split('\n');
|
|
27
|
+
// Check if any line contains .spck-editor/ (ignoring comments and whitespace)
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
const trimmed = line.trim();
|
|
30
|
+
// Skip empty lines and comments
|
|
31
|
+
if (trimmed === '' || trimmed.startsWith('#')) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// Check if line matches the directory pattern
|
|
35
|
+
if (trimmed === DIR_PATTERN) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
// If we can't read the file, assume it's not ignored
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Add .spck-editor/ to .gitignore
|
|
48
|
+
* Creates .gitignore if it doesn't exist
|
|
49
|
+
* Appends the pattern if not already present
|
|
50
|
+
*/
|
|
51
|
+
export function addSpckEditorToGitignore(directory) {
|
|
52
|
+
const gitignorePath = path.join(directory, '.gitignore');
|
|
53
|
+
// Check if already ignored
|
|
54
|
+
if (isSpckEditorIgnored(directory)) {
|
|
55
|
+
return; // Already present, nothing to do
|
|
56
|
+
}
|
|
57
|
+
let content = '';
|
|
58
|
+
if (fs.existsSync(gitignorePath)) {
|
|
59
|
+
// Read existing content
|
|
60
|
+
content = fs.readFileSync(gitignorePath, 'utf8');
|
|
61
|
+
// Ensure content ends with newline
|
|
62
|
+
if (content.length > 0 && !content.endsWith('\n')) {
|
|
63
|
+
content += '\n';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Add comment and pattern
|
|
67
|
+
const addition = `\n# Spck CLI project data\n${DIR_PATTERN}\n`;
|
|
68
|
+
// Write back to file
|
|
69
|
+
fs.writeFileSync(gitignorePath, content + addition, 'utf8');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the full path to .gitignore in a directory
|
|
73
|
+
*/
|
|
74
|
+
export function getGitignorePath(directory) {
|
|
75
|
+
return path.join(directory, '.gitignore');
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=gitignore.js.map
|