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.
Files changed (155) hide show
  1. package/.oxlintrc.json +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +631 -0
  4. package/bin/cli.js +20 -0
  5. package/bin/validate-cwd.js +41 -0
  6. package/dist/config/__tests__/config.test.d.ts +2 -0
  7. package/dist/config/__tests__/config.test.js +262 -0
  8. package/dist/config/__tests__/credentials.test.d.ts +2 -0
  9. package/dist/config/__tests__/credentials.test.js +360 -0
  10. package/dist/config/config.d.ts +33 -0
  11. package/dist/config/config.js +185 -0
  12. package/dist/config/credentials.d.ts +75 -0
  13. package/dist/config/credentials.js +259 -0
  14. package/dist/config/server-selection.d.ts +40 -0
  15. package/dist/config/server-selection.js +130 -0
  16. package/dist/connection/__tests__/firebase-auth.test.d.ts +2 -0
  17. package/dist/connection/__tests__/firebase-auth.test.js +96 -0
  18. package/dist/connection/__tests__/hmac.test.d.ts +2 -0
  19. package/dist/connection/__tests__/hmac.test.js +372 -0
  20. package/dist/connection/auth.d.ts +13 -0
  21. package/dist/connection/auth.js +91 -0
  22. package/dist/connection/firebase-auth.d.ts +40 -0
  23. package/dist/connection/firebase-auth.js +429 -0
  24. package/dist/connection/hmac.d.ts +24 -0
  25. package/dist/connection/hmac.js +109 -0
  26. package/dist/i18n/index.d.ts +25 -0
  27. package/dist/i18n/index.js +101 -0
  28. package/dist/i18n/locales/en.json +313 -0
  29. package/dist/i18n/locales/es.json +302 -0
  30. package/dist/i18n/locales/fr.json +302 -0
  31. package/dist/i18n/locales/id.json +302 -0
  32. package/dist/i18n/locales/ja.json +302 -0
  33. package/dist/i18n/locales/ko.json +302 -0
  34. package/dist/i18n/locales/locales/en.json +309 -0
  35. package/dist/i18n/locales/locales/es.json +302 -0
  36. package/dist/i18n/locales/locales/fr.json +302 -0
  37. package/dist/i18n/locales/locales/id.json +302 -0
  38. package/dist/i18n/locales/locales/ja.json +302 -0
  39. package/dist/i18n/locales/locales/ko.json +302 -0
  40. package/dist/i18n/locales/locales/pt.json +302 -0
  41. package/dist/i18n/locales/locales/zh-Hans.json +302 -0
  42. package/dist/i18n/locales/pt.json +302 -0
  43. package/dist/i18n/locales/zh-Hans.json +302 -0
  44. package/dist/index.d.ts +25 -0
  45. package/dist/index.js +493 -0
  46. package/dist/proxy/ProxyClient.d.ts +125 -0
  47. package/dist/proxy/ProxyClient.js +781 -0
  48. package/dist/proxy/ProxySocketWrapper.d.ts +43 -0
  49. package/dist/proxy/ProxySocketWrapper.js +98 -0
  50. package/dist/proxy/__tests__/ProxyClient.test.d.ts +2 -0
  51. package/dist/proxy/__tests__/ProxyClient.test.js +445 -0
  52. package/dist/proxy/__tests__/ProxySocketWrapper.test.d.ts +2 -0
  53. package/dist/proxy/__tests__/ProxySocketWrapper.test.js +190 -0
  54. package/dist/proxy/__tests__/handshake-validation.test.d.ts +2 -0
  55. package/dist/proxy/__tests__/handshake-validation.test.js +282 -0
  56. package/dist/proxy/__tests__/token-refresh-race.test.d.ts +14 -0
  57. package/dist/proxy/__tests__/token-refresh-race.test.js +173 -0
  58. package/dist/proxy/chunking.d.ts +53 -0
  59. package/dist/proxy/chunking.js +127 -0
  60. package/dist/proxy/handshake-validation.d.ts +21 -0
  61. package/dist/proxy/handshake-validation.js +49 -0
  62. package/dist/rpc/__tests__/router.test.d.ts +2 -0
  63. package/dist/rpc/__tests__/router.test.js +262 -0
  64. package/dist/rpc/router.d.ts +37 -0
  65. package/dist/rpc/router.js +132 -0
  66. package/dist/services/BrowserProxyService.d.ts +13 -0
  67. package/dist/services/BrowserProxyService.js +139 -0
  68. package/dist/services/FilesystemService.d.ts +99 -0
  69. package/dist/services/FilesystemService.js +742 -0
  70. package/dist/services/GitService.d.ts +243 -0
  71. package/dist/services/GitService.js +1439 -0
  72. package/dist/services/SearchService.d.ts +93 -0
  73. package/dist/services/SearchService.js +670 -0
  74. package/dist/services/TerminalService.d.ts +62 -0
  75. package/dist/services/TerminalService.js +337 -0
  76. package/dist/services/__tests__/BrowserProxyService.test.d.ts +2 -0
  77. package/dist/services/__tests__/BrowserProxyService.test.js +145 -0
  78. package/dist/services/__tests__/FilesystemService.test.d.ts +2 -0
  79. package/dist/services/__tests__/FilesystemService.test.js +609 -0
  80. package/dist/services/__tests__/GitService.test.d.ts +2 -0
  81. package/dist/services/__tests__/GitService.test.js +953 -0
  82. package/dist/services/__tests__/SearchService.test.d.ts +2 -0
  83. package/dist/services/__tests__/SearchService.test.js +384 -0
  84. package/dist/services/__tests__/TerminalService.test.d.ts +2 -0
  85. package/dist/services/__tests__/TerminalService.test.js +513 -0
  86. package/dist/setup/wizard.d.ts +10 -0
  87. package/dist/setup/wizard.js +172 -0
  88. package/dist/types.d.ts +196 -0
  89. package/dist/types.js +44 -0
  90. package/dist/utils/__tests__/gitignore.test.d.ts +2 -0
  91. package/dist/utils/__tests__/gitignore.test.js +127 -0
  92. package/dist/utils/gitignore.d.ts +24 -0
  93. package/dist/utils/gitignore.js +77 -0
  94. package/dist/utils/logger.d.ts +96 -0
  95. package/dist/utils/logger.js +456 -0
  96. package/dist/utils/project-dir.d.ts +51 -0
  97. package/dist/utils/project-dir.js +191 -0
  98. package/dist/utils/ripgrep.d.ts +34 -0
  99. package/dist/utils/ripgrep.js +148 -0
  100. package/dist/utils/tool-detection.d.ts +17 -0
  101. package/dist/utils/tool-detection.js +126 -0
  102. package/dist/watcher/FileWatcher.d.ts +10 -0
  103. package/dist/watcher/FileWatcher.js +42 -0
  104. package/package.json +70 -0
  105. package/src/config/__tests__/config.test.ts +318 -0
  106. package/src/config/__tests__/credentials.test.ts +494 -0
  107. package/src/config/config.ts +206 -0
  108. package/src/config/credentials.ts +302 -0
  109. package/src/config/server-selection.ts +150 -0
  110. package/src/connection/__tests__/firebase-auth.test.ts +121 -0
  111. package/src/connection/__tests__/hmac.test.ts +509 -0
  112. package/src/connection/auth.ts +140 -0
  113. package/src/connection/firebase-auth.ts +504 -0
  114. package/src/connection/hmac.ts +139 -0
  115. package/src/i18n/index.ts +119 -0
  116. package/src/i18n/locales/en.json +313 -0
  117. package/src/i18n/locales/es.json +302 -0
  118. package/src/i18n/locales/fr.json +302 -0
  119. package/src/i18n/locales/id.json +302 -0
  120. package/src/i18n/locales/ja.json +302 -0
  121. package/src/i18n/locales/ko.json +302 -0
  122. package/src/i18n/locales/pt.json +302 -0
  123. package/src/i18n/locales/zh-Hans.json +302 -0
  124. package/src/index.ts +542 -0
  125. package/src/proxy/ProxyClient.ts +968 -0
  126. package/src/proxy/ProxySocketWrapper.ts +113 -0
  127. package/src/proxy/__tests__/ProxyClient.test.ts +575 -0
  128. package/src/proxy/__tests__/ProxySocketWrapper.test.ts +251 -0
  129. package/src/proxy/__tests__/handshake-validation.test.ts +367 -0
  130. package/src/proxy/chunking.ts +162 -0
  131. package/src/proxy/handshake-validation.ts +64 -0
  132. package/src/rpc/__tests__/router.test.ts +400 -0
  133. package/src/rpc/router.ts +183 -0
  134. package/src/services/BrowserProxyService.ts +179 -0
  135. package/src/services/FilesystemService.ts +841 -0
  136. package/src/services/GitService.ts +1639 -0
  137. package/src/services/SearchService.ts +809 -0
  138. package/src/services/TerminalService.ts +413 -0
  139. package/src/services/__tests__/BrowserProxyService.test.ts +155 -0
  140. package/src/services/__tests__/FilesystemService.test.ts +1002 -0
  141. package/src/services/__tests__/GitService.test.ts +1552 -0
  142. package/src/services/__tests__/SearchService.test.ts +484 -0
  143. package/src/services/__tests__/TerminalService.test.ts +702 -0
  144. package/src/setup/wizard.ts +242 -0
  145. package/src/types/fossil-delta.d.ts +4 -0
  146. package/src/types.ts +287 -0
  147. package/src/utils/__tests__/gitignore.test.ts +174 -0
  148. package/src/utils/gitignore.ts +91 -0
  149. package/src/utils/logger.ts +578 -0
  150. package/src/utils/project-dir.ts +218 -0
  151. package/src/utils/ripgrep.ts +180 -0
  152. package/src/utils/tool-detection.ts +141 -0
  153. package/src/watcher/FileWatcher.ts +53 -0
  154. package/tsconfig.json +24 -0
  155. 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
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gitignore.test.d.ts.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