hostfn 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/core/backup.test.d.ts +2 -0
- package/dist/__tests__/core/backup.test.d.ts.map +1 -0
- package/dist/__tests__/core/backup.test.js +108 -0
- package/dist/__tests__/core/backup.test.js.map +1 -0
- package/dist/__tests__/core/health.test.d.ts +2 -0
- package/dist/__tests__/core/health.test.d.ts.map +1 -0
- package/dist/__tests__/core/health.test.js +97 -0
- package/dist/__tests__/core/health.test.js.map +1 -0
- package/dist/__tests__/core/lock.test.d.ts +2 -0
- package/dist/__tests__/core/lock.test.d.ts.map +1 -0
- package/dist/__tests__/core/lock.test.js +136 -0
- package/dist/__tests__/core/lock.test.js.map +1 -0
- package/dist/__tests__/core/nginx-multi-domain.test.d.ts +2 -0
- package/dist/__tests__/core/nginx-multi-domain.test.d.ts.map +1 -0
- package/dist/__tests__/core/nginx-multi-domain.test.js +158 -0
- package/dist/__tests__/core/nginx-multi-domain.test.js.map +1 -0
- package/dist/__tests__/runtimes/pm2.test.d.ts +2 -0
- package/dist/__tests__/runtimes/pm2.test.d.ts.map +1 -0
- package/dist/__tests__/runtimes/pm2.test.js +111 -0
- package/dist/__tests__/runtimes/pm2.test.js.map +1 -0
- package/dist/__tests__/utils/validation.test.d.ts +2 -0
- package/dist/__tests__/utils/validation.test.d.ts.map +1 -0
- package/dist/__tests__/utils/validation.test.js +136 -0
- package/dist/__tests__/utils/validation.test.js.map +1 -0
- package/dist/commands/deploy.d.ts +11 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +636 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/env.d.ts +21 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +317 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/expose.d.ts +6 -0
- package/dist/commands/expose.d.ts.map +1 -0
- package/dist/commands/expose.js +379 -0
- package/dist/commands/expose.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +175 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/logs.d.ts +10 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +75 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/rollback.d.ts +6 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +113 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/server/info.d.ts +2 -0
- package/dist/commands/server/info.d.ts.map +1 -0
- package/dist/commands/server/info.js +104 -0
- package/dist/commands/server/info.js.map +1 -0
- package/dist/commands/server/setup.d.ts +11 -0
- package/dist/commands/server/setup.d.ts.map +1 -0
- package/dist/commands/server/setup.js +161 -0
- package/dist/commands/server/setup.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +120 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/loader.d.ts +21 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +54 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +323 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +108 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/backup.d.ts +34 -0
- package/dist/core/backup.d.ts.map +1 -0
- package/dist/core/backup.js +95 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/health.d.ts +31 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +78 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/local.d.ts +19 -0
- package/dist/core/local.d.ts.map +1 -0
- package/dist/core/local.js +50 -0
- package/dist/core/local.js.map +1 -0
- package/dist/core/lock.d.ts +28 -0
- package/dist/core/lock.d.ts.map +1 -0
- package/dist/core/lock.js +89 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/nginx.d.ts +43 -0
- package/dist/core/nginx.d.ts.map +1 -0
- package/dist/core/nginx.js +131 -0
- package/dist/core/nginx.js.map +1 -0
- package/dist/core/ssh.d.ts +79 -0
- package/dist/core/ssh.d.ts.map +1 -0
- package/dist/core/ssh.js +264 -0
- package/dist/core/ssh.js.map +1 -0
- package/dist/core/sync.d.ts +25 -0
- package/dist/core/sync.d.ts.map +1 -0
- package/dist/core/sync.js +117 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/core/workspace.d.ts +13 -0
- package/dist/core/workspace.d.ts.map +1 -0
- package/dist/core/workspace.js +141 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/runtimes/base.d.ts +115 -0
- package/dist/runtimes/base.d.ts.map +1 -0
- package/dist/runtimes/base.js +16 -0
- package/dist/runtimes/base.js.map +1 -0
- package/dist/runtimes/nodejs/detector.d.ts +47 -0
- package/dist/runtimes/nodejs/detector.d.ts.map +1 -0
- package/dist/runtimes/nodejs/detector.js +143 -0
- package/dist/runtimes/nodejs/detector.js.map +1 -0
- package/dist/runtimes/nodejs/index.d.ts +14 -0
- package/dist/runtimes/nodejs/index.d.ts.map +1 -0
- package/dist/runtimes/nodejs/index.js +213 -0
- package/dist/runtimes/nodejs/index.js.map +1 -0
- package/dist/runtimes/nodejs/pm2.d.ts +17 -0
- package/dist/runtimes/nodejs/pm2.d.ts.map +1 -0
- package/dist/runtimes/nodejs/pm2.js +60 -0
- package/dist/runtimes/nodejs/pm2.js.map +1 -0
- package/dist/runtimes/registry.d.ts +34 -0
- package/dist/runtimes/registry.d.ts.map +1 -0
- package/dist/runtimes/registry.js +58 -0
- package/dist/runtimes/registry.js.map +1 -0
- package/dist/utils/logger.d.ts +47 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +76 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validation.d.ts +32 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +125 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +33 -16
- package/LICENSE +0 -21
- package/README.md +0 -1136
- package/_conduct/specs/1.v0.spec.md +0 -1041
- package/examples/express-api/package.json +0 -22
- package/examples/express-api/src/index.ts +0 -16
- package/examples/express-api/tsconfig.json +0 -11
- package/examples/github-actions-deploy.yml +0 -40
- package/examples/monorepo-config.json +0 -76
- package/examples/monorepo-multi-server-config.json +0 -74
- package/packages/cli/package.json +0 -40
- package/turbo.json +0 -24
- /package/{packages/cli/src → src}/__tests__/core/backup.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/core/health.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/core/lock.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/core/nginx-multi-domain.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/runtimes/pm2.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/utils/validation.test.ts +0 -0
- /package/{packages/cli/src → src}/commands/deploy.ts +0 -0
- /package/{packages/cli/src → src}/commands/env.ts +0 -0
- /package/{packages/cli/src → src}/commands/expose.ts +0 -0
- /package/{packages/cli/src → src}/commands/init.ts +0 -0
- /package/{packages/cli/src → src}/commands/logs.ts +0 -0
- /package/{packages/cli/src → src}/commands/rollback.ts +0 -0
- /package/{packages/cli/src → src}/commands/server/info.ts +0 -0
- /package/{packages/cli/src → src}/commands/server/setup.ts +0 -0
- /package/{packages/cli/src → src}/commands/status.ts +0 -0
- /package/{packages/cli/src → src}/config/loader.ts +0 -0
- /package/{packages/cli/src → src}/config/schema.ts +0 -0
- /package/{packages/cli/src → src}/core/backup.ts +0 -0
- /package/{packages/cli/src → src}/core/health.ts +0 -0
- /package/{packages/cli/src → src}/core/local.ts +0 -0
- /package/{packages/cli/src → src}/core/lock.ts +0 -0
- /package/{packages/cli/src → src}/core/nginx.ts +0 -0
- /package/{packages/cli/src → src}/core/ssh.ts +0 -0
- /package/{packages/cli/src → src}/core/sync.ts +0 -0
- /package/{packages/cli/src → src}/core/workspace.ts +0 -0
- /package/{packages/cli/src → src}/index.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/base.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/nodejs/detector.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/nodejs/index.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/nodejs/pm2.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/registry.ts +0 -0
- /package/{packages/cli/src → src}/utils/logger.ts +0 -0
- /package/{packages/cli/src → src}/utils/validation.ts +0 -0
- /package/{packages/cli/tsconfig.json → tsconfig.json} +0 -0
- /package/{packages/cli/vitest.config.ts → vitest.config.ts} +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export interface SSHConnectionOptions {
|
|
2
|
+
host: string;
|
|
3
|
+
port?: number;
|
|
4
|
+
username: string;
|
|
5
|
+
password?: string;
|
|
6
|
+
privateKeyPath?: string;
|
|
7
|
+
passphrase?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SSHCommandResult {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
exitCode: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* SSH Connection Manager
|
|
16
|
+
*/
|
|
17
|
+
export declare class SSHConnection {
|
|
18
|
+
private client;
|
|
19
|
+
private config;
|
|
20
|
+
private connected;
|
|
21
|
+
constructor(options: SSHConnectionOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Get default SSH key path (~/.ssh/id_rsa)
|
|
24
|
+
*/
|
|
25
|
+
private getDefaultKeyPath;
|
|
26
|
+
/**
|
|
27
|
+
* Connect to SSH server
|
|
28
|
+
*/
|
|
29
|
+
connect(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Execute command on remote server
|
|
32
|
+
*/
|
|
33
|
+
exec(command: string, options?: {
|
|
34
|
+
streaming?: boolean;
|
|
35
|
+
cwd?: string;
|
|
36
|
+
skipNvmSetup?: boolean;
|
|
37
|
+
}): Promise<SSHCommandResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Upload file to remote server (SCP)
|
|
40
|
+
*/
|
|
41
|
+
uploadFile(localPath: string, remotePath: string): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Download file from remote server
|
|
44
|
+
*/
|
|
45
|
+
downloadFile(remotePath: string, localPath: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Check if file/directory exists on remote
|
|
48
|
+
*/
|
|
49
|
+
exists(remotePath: string): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Create directory on remote server
|
|
52
|
+
*/
|
|
53
|
+
mkdir(remotePath: string, recursive?: boolean): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Disconnect from server
|
|
56
|
+
*/
|
|
57
|
+
disconnect(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Check if connected
|
|
60
|
+
*/
|
|
61
|
+
isConnected(): boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse SSH connection string (user@host)
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseSSHConnection(connectionString: string): {
|
|
67
|
+
username: string;
|
|
68
|
+
host: string;
|
|
69
|
+
port?: number;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Create SSH connection from connection string
|
|
73
|
+
*/
|
|
74
|
+
export declare function createSSHConnection(connectionString: string, options?: {
|
|
75
|
+
password?: string;
|
|
76
|
+
privateKeyPath?: string;
|
|
77
|
+
passphrase?: string;
|
|
78
|
+
}): Promise<SSHConnection>;
|
|
79
|
+
//# sourceMappingURL=ssh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../src/core/ssh.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAkB;gBAEvB,OAAO,EAAE,oBAAoB;IAoDzC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAmBzB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAc9B;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QACpC,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAsD7B;;OAEG;IACG,UAAU,CACd,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAuBhB;;OAEG;IACG,YAAY,CAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAuBhB;;OAEG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASlD;;OAEG;IACG,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,OAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IASzE;;OAEG;IACH,UAAU,IAAI,IAAI;IAOlB;;OAEG;IACH,WAAW,IAAI,OAAO;CAGvB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,GAAG;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAkBA;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,gBAAgB,EAAE,MAAM,EACxB,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,aAAa,CAAC,CAgBxB"}
|
package/dist/core/ssh.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Client } from 'ssh2';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
/**
|
|
6
|
+
* SSH Connection Manager
|
|
7
|
+
*/
|
|
8
|
+
export class SSHConnection {
|
|
9
|
+
client;
|
|
10
|
+
config;
|
|
11
|
+
connected = false;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.client = new Client();
|
|
14
|
+
// Build SSH config
|
|
15
|
+
this.config = {
|
|
16
|
+
host: options.host,
|
|
17
|
+
port: options.port || 22,
|
|
18
|
+
username: options.username,
|
|
19
|
+
};
|
|
20
|
+
// Add authentication method
|
|
21
|
+
if (options.password) {
|
|
22
|
+
this.config.password = options.password;
|
|
23
|
+
}
|
|
24
|
+
else if (process.env.HOSTFN_SSH_KEY) {
|
|
25
|
+
// CI/CD mode: Load key from environment variable
|
|
26
|
+
try {
|
|
27
|
+
this.config.privateKey = Buffer.from(process.env.HOSTFN_SSH_KEY, 'base64');
|
|
28
|
+
if (options.passphrase || process.env.HOSTFN_SSH_PASSPHRASE) {
|
|
29
|
+
this.config.passphrase = options.passphrase || process.env.HOSTFN_SSH_PASSPHRASE;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new Error(`Failed to decode HOSTFN_SSH_KEY from environment variable\n` +
|
|
34
|
+
`Make sure it's base64 encoded: cat ~/.ssh/id_rsa | base64`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Enable SSH agent for passphrase-protected keys
|
|
39
|
+
if (process.env.SSH_AUTH_SOCK) {
|
|
40
|
+
this.config.agent = process.env.SSH_AUTH_SOCK;
|
|
41
|
+
}
|
|
42
|
+
// Try to load private key from file
|
|
43
|
+
const keyPath = options.privateKeyPath || this.getDefaultKeyPath();
|
|
44
|
+
try {
|
|
45
|
+
this.config.privateKey = readFileSync(keyPath);
|
|
46
|
+
if (options.passphrase) {
|
|
47
|
+
this.config.passphrase = options.passphrase;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new Error(`Failed to load SSH key from ${keyPath}\n` +
|
|
52
|
+
`Make sure the file exists or set HOSTFN_SSH_KEY environment variable\n` +
|
|
53
|
+
`For CI/CD: export HOSTFN_SSH_KEY=$(cat ~/.ssh/id_rsa | base64)`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get default SSH key path (~/.ssh/id_rsa)
|
|
59
|
+
*/
|
|
60
|
+
getDefaultKeyPath() {
|
|
61
|
+
const sshDir = resolve(homedir(), '.ssh');
|
|
62
|
+
// Try common key names
|
|
63
|
+
const keyNames = ['id_rsa', 'id_ed25519', 'id_ecdsa'];
|
|
64
|
+
for (const keyName of keyNames) {
|
|
65
|
+
const keyPath = resolve(sshDir, keyName);
|
|
66
|
+
try {
|
|
67
|
+
readFileSync(keyPath);
|
|
68
|
+
return keyPath;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Default to id_rsa even if it doesn't exist (will error later)
|
|
75
|
+
return resolve(sshDir, 'id_rsa');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Connect to SSH server
|
|
79
|
+
*/
|
|
80
|
+
async connect() {
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
this.client
|
|
83
|
+
.on('ready', () => {
|
|
84
|
+
this.connected = true;
|
|
85
|
+
resolve();
|
|
86
|
+
})
|
|
87
|
+
.on('error', (err) => {
|
|
88
|
+
reject(new Error(`SSH connection failed: ${err.message}`));
|
|
89
|
+
})
|
|
90
|
+
.connect(this.config);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Execute command on remote server
|
|
95
|
+
*/
|
|
96
|
+
async exec(command, options) {
|
|
97
|
+
if (!this.connected) {
|
|
98
|
+
throw new Error('Not connected. Call connect() first.');
|
|
99
|
+
}
|
|
100
|
+
// Source nvm to ensure Node.js/npm are in PATH for non-interactive sessions
|
|
101
|
+
// Skip for setup scripts that install nvm
|
|
102
|
+
const nvmSetup = options?.skipNvmSetup
|
|
103
|
+
? ''
|
|
104
|
+
: 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && ';
|
|
105
|
+
// Add cd command if cwd provided
|
|
106
|
+
const fullCommand = options?.cwd
|
|
107
|
+
? `${nvmSetup}cd ${options.cwd} && ${command}`
|
|
108
|
+
: `${nvmSetup}${command}`;
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
this.client.exec(fullCommand, (err, stream) => {
|
|
111
|
+
if (err) {
|
|
112
|
+
reject(new Error(`Failed to execute command: ${err.message}`));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
let stdout = '';
|
|
116
|
+
let stderr = '';
|
|
117
|
+
stream
|
|
118
|
+
.on('close', (code) => {
|
|
119
|
+
resolve({
|
|
120
|
+
stdout,
|
|
121
|
+
stderr,
|
|
122
|
+
exitCode: code,
|
|
123
|
+
});
|
|
124
|
+
})
|
|
125
|
+
.on('data', (data) => {
|
|
126
|
+
const output = data.toString();
|
|
127
|
+
stdout += output;
|
|
128
|
+
if (options?.streaming) {
|
|
129
|
+
process.stdout.write(output);
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.stderr.on('data', (data) => {
|
|
133
|
+
const output = data.toString();
|
|
134
|
+
stderr += output;
|
|
135
|
+
if (options?.streaming) {
|
|
136
|
+
process.stderr.write(output);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Upload file to remote server (SCP)
|
|
144
|
+
*/
|
|
145
|
+
async uploadFile(localPath, remotePath) {
|
|
146
|
+
if (!this.connected) {
|
|
147
|
+
throw new Error('Not connected. Call connect() first.');
|
|
148
|
+
}
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
this.client.sftp((err, sftp) => {
|
|
151
|
+
if (err) {
|
|
152
|
+
reject(new Error(`SFTP session failed: ${err.message}`));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
sftp.fastPut(localPath, remotePath, (err) => {
|
|
156
|
+
if (err) {
|
|
157
|
+
reject(new Error(`Failed to upload file: ${err.message}`));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
resolve();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Download file from remote server
|
|
168
|
+
*/
|
|
169
|
+
async downloadFile(remotePath, localPath) {
|
|
170
|
+
if (!this.connected) {
|
|
171
|
+
throw new Error('Not connected. Call connect() first.');
|
|
172
|
+
}
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
this.client.sftp((err, sftp) => {
|
|
175
|
+
if (err) {
|
|
176
|
+
reject(new Error(`SFTP session failed: ${err.message}`));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
sftp.fastGet(remotePath, localPath, (err) => {
|
|
180
|
+
if (err) {
|
|
181
|
+
reject(new Error(`Failed to download file: ${err.message}`));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
resolve();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Check if file/directory exists on remote
|
|
192
|
+
*/
|
|
193
|
+
async exists(remotePath) {
|
|
194
|
+
try {
|
|
195
|
+
const result = await this.exec(`test -e ${remotePath} && echo "exists"`);
|
|
196
|
+
return result.stdout.trim() === 'exists';
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create directory on remote server
|
|
204
|
+
*/
|
|
205
|
+
async mkdir(remotePath, recursive = true) {
|
|
206
|
+
const flag = recursive ? '-p' : '';
|
|
207
|
+
const result = await this.exec(`mkdir ${flag} ${remotePath}`);
|
|
208
|
+
if (result.exitCode !== 0) {
|
|
209
|
+
throw new Error(`Failed to create directory: ${result.stderr}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Disconnect from server
|
|
214
|
+
*/
|
|
215
|
+
disconnect() {
|
|
216
|
+
if (this.connected) {
|
|
217
|
+
this.client.end();
|
|
218
|
+
this.connected = false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Check if connected
|
|
223
|
+
*/
|
|
224
|
+
isConnected() {
|
|
225
|
+
return this.connected;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Parse SSH connection string (user@host)
|
|
230
|
+
*/
|
|
231
|
+
export function parseSSHConnection(connectionString) {
|
|
232
|
+
// Support formats:
|
|
233
|
+
// - user@host
|
|
234
|
+
// - user@host:port
|
|
235
|
+
const match = connectionString.match(/^([^@]+)@([^:]+)(?::(\d+))?$/);
|
|
236
|
+
if (!match) {
|
|
237
|
+
throw new Error(`Invalid SSH connection string: ${connectionString}\n` +
|
|
238
|
+
`Expected format: user@host or user@host:port`);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
username: match[1],
|
|
242
|
+
host: match[2],
|
|
243
|
+
port: match[3] ? parseInt(match[3]) : undefined,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Create SSH connection from connection string
|
|
248
|
+
*/
|
|
249
|
+
export async function createSSHConnection(connectionString, options) {
|
|
250
|
+
// Support HOSTFN_HOST env var for CI/CD
|
|
251
|
+
const hostToUse = process.env.HOSTFN_HOST || connectionString;
|
|
252
|
+
const { username, host, port } = parseSSHConnection(hostToUse);
|
|
253
|
+
const ssh = new SSHConnection({
|
|
254
|
+
host,
|
|
255
|
+
port,
|
|
256
|
+
username,
|
|
257
|
+
password: options?.password || process.env.HOSTFN_SSH_PASSWORD,
|
|
258
|
+
privateKeyPath: options?.privateKeyPath,
|
|
259
|
+
passphrase: options?.passphrase || process.env.HOSTFN_SSH_PASSPHRASE,
|
|
260
|
+
});
|
|
261
|
+
await ssh.connect();
|
|
262
|
+
return ssh;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=ssh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/core/ssh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface SyncOptions {
|
|
2
|
+
exclude?: string[];
|
|
3
|
+
include?: string[];
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
verbose?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Sync files to remote server using rsync
|
|
9
|
+
*/
|
|
10
|
+
export declare class FileSync {
|
|
11
|
+
/**
|
|
12
|
+
* Sync local directory to remote server
|
|
13
|
+
*/
|
|
14
|
+
static sync(localPath: string, remotePath: string, sshConnection: string, // user@host
|
|
15
|
+
options?: SyncOptions): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Check if rsync is available
|
|
18
|
+
*/
|
|
19
|
+
static isRsyncAvailable(): Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Get rsync version
|
|
22
|
+
*/
|
|
23
|
+
static getRsyncVersion(): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/core/sync.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB;;OAEG;WACU,IAAI,CACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EAAE,YAAY;IACnC,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,IAAI,CAAC;IA2FhB;;OAEG;WACU,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IASjD;;OAEG;WACU,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;CAShD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
/**
|
|
3
|
+
* Sync files to remote server using rsync
|
|
4
|
+
*/
|
|
5
|
+
export class FileSync {
|
|
6
|
+
/**
|
|
7
|
+
* Sync local directory to remote server
|
|
8
|
+
*/
|
|
9
|
+
static async sync(localPath, remotePath, sshConnection, // user@host
|
|
10
|
+
options = {}) {
|
|
11
|
+
const args = [
|
|
12
|
+
'-avz', // archive, verbose, compress
|
|
13
|
+
'--delete', // delete files on remote that don't exist locally
|
|
14
|
+
];
|
|
15
|
+
// Handle SSH authentication for CI/CD mode
|
|
16
|
+
let tempKeyPath;
|
|
17
|
+
if (process.env.HOSTFN_SSH_KEY) {
|
|
18
|
+
// CI/CD mode: create temporary key file from base64-encoded env var
|
|
19
|
+
const { writeFileSync, mkdtempSync } = await import('fs');
|
|
20
|
+
const { join } = await import('path');
|
|
21
|
+
const { tmpdir } = await import('os');
|
|
22
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'hostfn-ssh-'));
|
|
23
|
+
tempKeyPath = join(tempDir, 'id_rsa');
|
|
24
|
+
// Decode and write the SSH key
|
|
25
|
+
const keyBuffer = Buffer.from(process.env.HOSTFN_SSH_KEY, 'base64');
|
|
26
|
+
writeFileSync(tempKeyPath, keyBuffer, { mode: 0o600 });
|
|
27
|
+
// Build SSH command with the temporary key
|
|
28
|
+
const sshCmd = `ssh -i ${tempKeyPath} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes`;
|
|
29
|
+
args.push('-e', sshCmd);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Local mode: use default SSH config (respects ~/.ssh/config and ssh-agent)
|
|
33
|
+
args.push('-e', 'ssh -o StrictHostKeyChecking=no -o BatchMode=yes');
|
|
34
|
+
}
|
|
35
|
+
// Add dry-run flag
|
|
36
|
+
if (options.dryRun) {
|
|
37
|
+
args.push('--dry-run');
|
|
38
|
+
}
|
|
39
|
+
// Add exclude patterns
|
|
40
|
+
if (options.exclude && options.exclude.length > 0) {
|
|
41
|
+
for (const pattern of options.exclude) {
|
|
42
|
+
args.push('--exclude', pattern);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Add include patterns
|
|
46
|
+
if (options.include && options.include.length > 0) {
|
|
47
|
+
for (const pattern of options.include) {
|
|
48
|
+
args.push('--include', pattern);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Ensure trailing slash on source (rsync behavior)
|
|
52
|
+
const source = localPath.endsWith('/') ? localPath : `${localPath}/`;
|
|
53
|
+
const destination = `${sshConnection}:${remotePath}`;
|
|
54
|
+
args.push(source, destination);
|
|
55
|
+
try {
|
|
56
|
+
const result = await execa('rsync', args, {
|
|
57
|
+
stdio: options.verbose ? 'inherit' : 'pipe',
|
|
58
|
+
});
|
|
59
|
+
if (result.exitCode !== 0) {
|
|
60
|
+
throw new Error(`rsync failed with exit code ${result.exitCode}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error instanceof Error) {
|
|
65
|
+
// Check if rsync is not installed
|
|
66
|
+
if (error.message.includes('ENOENT') || error.message.includes('not found')) {
|
|
67
|
+
throw new Error('rsync is not installed on your system.\n' +
|
|
68
|
+
'Please install it:\n' +
|
|
69
|
+
' - macOS: brew install rsync\n' +
|
|
70
|
+
' - Ubuntu/Debian: apt-get install rsync\n' +
|
|
71
|
+
' - Windows: Install via WSL or use Git Bash');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
// Cleanup temporary key file if it was created
|
|
78
|
+
if (tempKeyPath) {
|
|
79
|
+
try {
|
|
80
|
+
const { unlinkSync, rmdirSync } = await import('fs');
|
|
81
|
+
const { dirname } = await import('path');
|
|
82
|
+
unlinkSync(tempKeyPath);
|
|
83
|
+
rmdirSync(dirname(tempKeyPath));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Ignore cleanup errors
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if rsync is available
|
|
93
|
+
*/
|
|
94
|
+
static async isRsyncAvailable() {
|
|
95
|
+
try {
|
|
96
|
+
await execa('rsync', ['--version']);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get rsync version
|
|
105
|
+
*/
|
|
106
|
+
static async getRsyncVersion() {
|
|
107
|
+
try {
|
|
108
|
+
const result = await execa('rsync', ['--version']);
|
|
109
|
+
const match = result.stdout.match(/rsync\s+version\s+([\d.]+)/);
|
|
110
|
+
return match ? match[1] : 'unknown';
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return 'not installed';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/core/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class WorkspaceManager {
|
|
2
|
+
private workspaceRoot;
|
|
3
|
+
private workspacePackages;
|
|
4
|
+
detectWorkspace(cwd: string): Promise<boolean>;
|
|
5
|
+
private indexWorkspacePackages;
|
|
6
|
+
getWorkspaceDependencies(servicePath: string): string[];
|
|
7
|
+
bundleWorkspaceDependencies(servicePath: string, targetDir: string): Promise<void>;
|
|
8
|
+
rewritePackageJson(servicePath: string, targetDir: string): void;
|
|
9
|
+
private readPackageJson;
|
|
10
|
+
getWorkspaceRoot(): string | null;
|
|
11
|
+
isInWorkspace(): boolean;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/core/workspace.ts"],"names":[],"mappings":"AAaA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,iBAAiB,CAAkC;IAErD,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAuBtC,sBAAsB;IA0BpC,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE;IAsBjD,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CxF,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IA6BhE,OAAO,CAAC,eAAe;IAWvB,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC,aAAa,IAAI,OAAO;CAGzB"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, cpSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join, dirname, resolve } from 'path';
|
|
3
|
+
import { Logger } from '../utils/logger.js';
|
|
4
|
+
export class WorkspaceManager {
|
|
5
|
+
workspaceRoot = null;
|
|
6
|
+
workspacePackages = new Map();
|
|
7
|
+
async detectWorkspace(cwd) {
|
|
8
|
+
let currentDir = cwd;
|
|
9
|
+
const root = resolve('/');
|
|
10
|
+
while (currentDir !== root) {
|
|
11
|
+
const pkgPath = join(currentDir, 'package.json');
|
|
12
|
+
if (existsSync(pkgPath)) {
|
|
13
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
14
|
+
if (pkg.workspaces) {
|
|
15
|
+
this.workspaceRoot = currentDir;
|
|
16
|
+
await this.indexWorkspacePackages();
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
currentDir = dirname(currentDir);
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
async indexWorkspacePackages() {
|
|
25
|
+
if (!this.workspaceRoot)
|
|
26
|
+
return;
|
|
27
|
+
const rootPkg = this.readPackageJson(this.workspaceRoot);
|
|
28
|
+
if (!rootPkg?.workspaces)
|
|
29
|
+
return;
|
|
30
|
+
const workspaces = Array.isArray(rootPkg.workspaces)
|
|
31
|
+
? rootPkg.workspaces
|
|
32
|
+
: rootPkg.workspaces.packages;
|
|
33
|
+
const glob = await import('fast-glob');
|
|
34
|
+
for (const pattern of workspaces) {
|
|
35
|
+
const matches = await glob.default(join(this.workspaceRoot, pattern, 'package.json'), {
|
|
36
|
+
absolute: true,
|
|
37
|
+
});
|
|
38
|
+
for (const pkgPath of matches) {
|
|
39
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
40
|
+
if (pkg.name) {
|
|
41
|
+
this.workspacePackages.set(pkg.name, dirname(pkgPath));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
getWorkspaceDependencies(servicePath) {
|
|
47
|
+
const pkg = this.readPackageJson(servicePath);
|
|
48
|
+
if (!pkg)
|
|
49
|
+
return [];
|
|
50
|
+
const allDeps = {
|
|
51
|
+
...pkg.dependencies,
|
|
52
|
+
...pkg.devDependencies,
|
|
53
|
+
};
|
|
54
|
+
const workspaceDeps = [];
|
|
55
|
+
for (const [depName, depVersion] of Object.entries(allDeps)) {
|
|
56
|
+
if (depVersion === '*' || depVersion === 'workspace:*' || depVersion.startsWith('workspace:')) {
|
|
57
|
+
if (this.workspacePackages.has(depName)) {
|
|
58
|
+
workspaceDeps.push(depName);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return workspaceDeps;
|
|
63
|
+
}
|
|
64
|
+
async bundleWorkspaceDependencies(servicePath, targetDir) {
|
|
65
|
+
const workspaceDeps = this.getWorkspaceDependencies(servicePath);
|
|
66
|
+
if (workspaceDeps.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
Logger.info(`Bundling ${workspaceDeps.length} workspace dependencies...`);
|
|
70
|
+
const nodeModulesDir = join(targetDir, 'node_modules');
|
|
71
|
+
mkdirSync(nodeModulesDir, { recursive: true });
|
|
72
|
+
for (const depName of workspaceDeps) {
|
|
73
|
+
const depPath = this.workspacePackages.get(depName);
|
|
74
|
+
if (!depPath)
|
|
75
|
+
continue;
|
|
76
|
+
const depPkg = this.readPackageJson(depPath);
|
|
77
|
+
if (depPkg?.scripts?.build) {
|
|
78
|
+
Logger.log(` → Building ${depName}...`);
|
|
79
|
+
try {
|
|
80
|
+
const { execSync } = await import('child_process');
|
|
81
|
+
execSync('npm run build', { cwd: depPath, stdio: 'ignore' });
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
Logger.warn(` ⚠ Failed to build ${depName}, bundling as-is`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const targetDepDir = join(nodeModulesDir, depName);
|
|
88
|
+
mkdirSync(dirname(targetDepDir), { recursive: true });
|
|
89
|
+
cpSync(depPath, targetDepDir, {
|
|
90
|
+
recursive: true,
|
|
91
|
+
filter: (src) => {
|
|
92
|
+
const relativePath = src.replace(depPath, '');
|
|
93
|
+
return !relativePath.includes('node_modules') &&
|
|
94
|
+
!relativePath.includes('.git');
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
Logger.log(` ✓ Bundled ${depName}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
rewritePackageJson(servicePath, targetDir) {
|
|
101
|
+
const workspaceDeps = this.getWorkspaceDependencies(servicePath);
|
|
102
|
+
if (workspaceDeps.length === 0) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const pkgPath = join(targetDir, 'package.json');
|
|
106
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
107
|
+
if (pkg.dependencies) {
|
|
108
|
+
for (const depName of workspaceDeps) {
|
|
109
|
+
if (pkg.dependencies[depName]) {
|
|
110
|
+
pkg.dependencies[depName] = `file:./node_modules/${depName}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (pkg.devDependencies) {
|
|
115
|
+
for (const depName of workspaceDeps) {
|
|
116
|
+
if (pkg.devDependencies[depName]) {
|
|
117
|
+
delete pkg.devDependencies[depName];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
122
|
+
}
|
|
123
|
+
readPackageJson(dir) {
|
|
124
|
+
const pkgPath = join(dir, 'package.json');
|
|
125
|
+
if (!existsSync(pkgPath))
|
|
126
|
+
return null;
|
|
127
|
+
try {
|
|
128
|
+
return JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
getWorkspaceRoot() {
|
|
135
|
+
return this.workspaceRoot;
|
|
136
|
+
}
|
|
137
|
+
isInWorkspace() {
|
|
138
|
+
return this.workspaceRoot !== null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=workspace.js.map
|