ctx-sync 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/audit.d.ts +76 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +367 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/config.d.ts +58 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +114 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/dir.d.ts +56 -0
- package/dist/commands/dir.d.ts.map +1 -0
- package/dist/commands/dir.js +172 -0
- package/dist/commands/dir.js.map +1 -0
- package/dist/commands/docker.d.ts +140 -0
- package/dist/commands/docker.d.ts.map +1 -0
- package/dist/commands/docker.js +380 -0
- package/dist/commands/docker.js.map +1 -0
- package/dist/commands/env.d.ts +96 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +352 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/init.d.ts +89 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +272 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/key.d.ts +92 -0
- package/dist/commands/key.d.ts.map +1 -0
- package/dist/commands/key.js +274 -0
- package/dist/commands/key.js.map +1 -0
- package/dist/commands/list.d.ts +38 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +84 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/note.d.ts +151 -0
- package/dist/commands/note.d.ts.map +1 -0
- package/dist/commands/note.js +411 -0
- package/dist/commands/note.js.map +1 -0
- package/dist/commands/pull.d.ts +47 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +94 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +40 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +94 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/restore.d.ts +116 -0
- package/dist/commands/restore.d.ts.map +1 -0
- package/dist/commands/restore.js +336 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/service.d.ts +83 -0
- package/dist/commands/service.d.ts.map +1 -0
- package/dist/commands/service.js +259 -0
- package/dist/commands/service.js.map +1 -0
- package/dist/commands/show.d.ts +63 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +243 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/status.d.ts +53 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +150 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +105 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +243 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/team.d.ts +79 -0
- package/dist/commands/team.d.ts.map +1 -0
- package/dist/commands/team.js +233 -0
- package/dist/commands/team.js.map +1 -0
- package/dist/commands/track.d.ts +109 -0
- package/dist/commands/track.d.ts.map +1 -0
- package/dist/commands/track.js +406 -0
- package/dist/commands/track.js.map +1 -0
- package/dist/core/command-validator.d.ts +100 -0
- package/dist/core/command-validator.d.ts.map +1 -0
- package/dist/core/command-validator.js +299 -0
- package/dist/core/command-validator.js.map +1 -0
- package/dist/core/config-store.d.ts +76 -0
- package/dist/core/config-store.d.ts.map +1 -0
- package/dist/core/config-store.js +148 -0
- package/dist/core/config-store.js.map +1 -0
- package/dist/core/directories-handler.d.ts +116 -0
- package/dist/core/directories-handler.d.ts.map +1 -0
- package/dist/core/directories-handler.js +199 -0
- package/dist/core/directories-handler.js.map +1 -0
- package/dist/core/docker-handler.d.ts +183 -0
- package/dist/core/docker-handler.d.ts.map +1 -0
- package/dist/core/docker-handler.js +515 -0
- package/dist/core/docker-handler.js.map +1 -0
- package/dist/core/encryption.d.ts +79 -0
- package/dist/core/encryption.d.ts.map +1 -0
- package/dist/core/encryption.js +111 -0
- package/dist/core/encryption.js.map +1 -0
- package/dist/core/env-handler.d.ts +128 -0
- package/dist/core/env-handler.d.ts.map +1 -0
- package/dist/core/env-handler.js +272 -0
- package/dist/core/env-handler.js.map +1 -0
- package/dist/core/git-sync.d.ts +88 -0
- package/dist/core/git-sync.d.ts.map +1 -0
- package/dist/core/git-sync.js +143 -0
- package/dist/core/git-sync.js.map +1 -0
- package/dist/core/key-store.d.ts +51 -0
- package/dist/core/key-store.d.ts.map +1 -0
- package/dist/core/key-store.js +108 -0
- package/dist/core/key-store.js.map +1 -0
- package/dist/core/log-sanitizer.d.ts +72 -0
- package/dist/core/log-sanitizer.d.ts.map +1 -0
- package/dist/core/log-sanitizer.js +202 -0
- package/dist/core/log-sanitizer.js.map +1 -0
- package/dist/core/path-validator.d.ts +37 -0
- package/dist/core/path-validator.d.ts.map +1 -0
- package/dist/core/path-validator.js +127 -0
- package/dist/core/path-validator.js.map +1 -0
- package/dist/core/recipients.d.ts +99 -0
- package/dist/core/recipients.d.ts.map +1 -0
- package/dist/core/recipients.js +206 -0
- package/dist/core/recipients.js.map +1 -0
- package/dist/core/services-handler.d.ts +113 -0
- package/dist/core/services-handler.d.ts.map +1 -0
- package/dist/core/services-handler.js +176 -0
- package/dist/core/services-handler.js.map +1 -0
- package/dist/core/state-manager.d.ts +96 -0
- package/dist/core/state-manager.d.ts.map +1 -0
- package/dist/core/state-manager.js +165 -0
- package/dist/core/state-manager.js.map +1 -0
- package/dist/core/transport.d.ts +28 -0
- package/dist/core/transport.d.ts.map +1 -0
- package/dist/core/transport.js +79 -0
- package/dist/core/transport.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/errors.d.ts +81 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +191 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/secure-memory.d.ts +65 -0
- package/dist/utils/secure-memory.d.ts.map +1 -0
- package/dist/utils/secure-memory.js +86 -0
- package/dist/utils/secure-memory.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Age encryption wrapper module.
|
|
3
|
+
*
|
|
4
|
+
* Provides key generation, encryption, and decryption operations
|
|
5
|
+
* using the Age encryption library. All operations are in-memory only —
|
|
6
|
+
* no temporary files are created during crypto operations.
|
|
7
|
+
*
|
|
8
|
+
* @module core/encryption
|
|
9
|
+
*/
|
|
10
|
+
import { Encrypter, Decrypter, generateIdentity, identityToRecipient } from 'age-encryption';
|
|
11
|
+
import { armor } from 'age-encryption';
|
|
12
|
+
/**
|
|
13
|
+
* Generate a new Age key pair.
|
|
14
|
+
*
|
|
15
|
+
* @returns Object containing the public key (age1...) and private key (AGE-SECRET-KEY-...).
|
|
16
|
+
*/
|
|
17
|
+
export async function generateKey() {
|
|
18
|
+
const privateKey = await generateIdentity();
|
|
19
|
+
const publicKey = await identityToRecipient(privateKey);
|
|
20
|
+
return { publicKey, privateKey };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Encrypt a plaintext string with an Age public key.
|
|
24
|
+
*
|
|
25
|
+
* @param plaintext - The string to encrypt.
|
|
26
|
+
* @param publicKey - The Age public key (age1...) to encrypt for.
|
|
27
|
+
* @returns ASCII-armored Age ciphertext.
|
|
28
|
+
* @throws If the public key is invalid.
|
|
29
|
+
*/
|
|
30
|
+
export async function encrypt(plaintext, publicKey) {
|
|
31
|
+
const encrypter = new Encrypter();
|
|
32
|
+
encrypter.addRecipient(publicKey);
|
|
33
|
+
const encrypted = await encrypter.encrypt(plaintext);
|
|
34
|
+
return armor.encode(encrypted);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Decrypt an ASCII-armored Age ciphertext with a private key.
|
|
38
|
+
*
|
|
39
|
+
* @param ciphertext - The ASCII-armored Age ciphertext.
|
|
40
|
+
* @param privateKey - The Age private key (AGE-SECRET-KEY-...).
|
|
41
|
+
* @returns The decrypted plaintext string.
|
|
42
|
+
* @throws If the private key cannot decrypt the ciphertext.
|
|
43
|
+
*/
|
|
44
|
+
export async function decrypt(ciphertext, privateKey) {
|
|
45
|
+
const decrypter = new Decrypter();
|
|
46
|
+
decrypter.addIdentity(privateKey);
|
|
47
|
+
const decoded = armor.decode(ciphertext);
|
|
48
|
+
return decrypter.decrypt(decoded, 'text');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Encrypt a typed data object as JSON into an Age ciphertext blob.
|
|
52
|
+
* The data is serialised to JSON in memory, then encrypted.
|
|
53
|
+
* No plaintext JSON is ever written to disk.
|
|
54
|
+
*
|
|
55
|
+
* @param data - The data to encrypt.
|
|
56
|
+
* @param publicKey - The Age public key to encrypt for.
|
|
57
|
+
* @returns ASCII-armored Age ciphertext containing the serialised JSON.
|
|
58
|
+
*/
|
|
59
|
+
export async function encryptState(data, publicKey) {
|
|
60
|
+
const json = JSON.stringify(data);
|
|
61
|
+
return encrypt(json, publicKey);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Decrypt an Age ciphertext blob and parse the JSON back into a typed object.
|
|
65
|
+
*
|
|
66
|
+
* @param ciphertext - The ASCII-armored Age ciphertext.
|
|
67
|
+
* @param privateKey - The Age private key.
|
|
68
|
+
* @returns The decrypted and parsed data.
|
|
69
|
+
* @throws If decryption fails or the JSON is invalid.
|
|
70
|
+
*/
|
|
71
|
+
export async function decryptState(ciphertext, privateKey) {
|
|
72
|
+
const json = await decrypt(ciphertext, privateKey);
|
|
73
|
+
return JSON.parse(json);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Encrypt a plaintext string for multiple Age recipients.
|
|
77
|
+
*
|
|
78
|
+
* All recipients can independently decrypt the resulting ciphertext
|
|
79
|
+
* using their own private key. This enables team/multi-machine support.
|
|
80
|
+
*
|
|
81
|
+
* @param plaintext - The string to encrypt.
|
|
82
|
+
* @param publicKeys - Array of Age public keys (age1...) to encrypt for.
|
|
83
|
+
* @returns ASCII-armored Age ciphertext.
|
|
84
|
+
* @throws If any public key is invalid or the array is empty.
|
|
85
|
+
*/
|
|
86
|
+
export async function encryptForRecipients(plaintext, publicKeys) {
|
|
87
|
+
if (publicKeys.length === 0) {
|
|
88
|
+
throw new Error('At least one recipient public key is required.');
|
|
89
|
+
}
|
|
90
|
+
const encrypter = new Encrypter();
|
|
91
|
+
for (const key of publicKeys) {
|
|
92
|
+
encrypter.addRecipient(key);
|
|
93
|
+
}
|
|
94
|
+
const encrypted = await encrypter.encrypt(plaintext);
|
|
95
|
+
return armor.encode(encrypted);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Encrypt a typed data object as JSON for multiple Age recipients.
|
|
99
|
+
*
|
|
100
|
+
* Serialises the data to JSON in memory, then encrypts for all recipients.
|
|
101
|
+
* No plaintext JSON is ever written to disk.
|
|
102
|
+
*
|
|
103
|
+
* @param data - The data to encrypt.
|
|
104
|
+
* @param publicKeys - Array of Age public keys to encrypt for.
|
|
105
|
+
* @returns ASCII-armored Age ciphertext containing the serialised JSON.
|
|
106
|
+
*/
|
|
107
|
+
export async function encryptStateForRecipients(data, publicKeys) {
|
|
108
|
+
const json = JSON.stringify(data);
|
|
109
|
+
return encryptForRecipients(json, publicKeys);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=encryption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/core/encryption.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,UAAU,GAAG,MAAM,gBAAgB,EAAE,CAAC;IAC5C,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACxD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,SAAiB,EAAE,SAAiB;IAChE,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;IAClC,SAAS,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,UAAkB,EAAE,UAAkB;IAClE,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;IAClC,SAAS,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,IAAO,EAAE,SAAiB;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,UAAkB,EAAE,UAAkB;IAC1E,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,UAAoB;IAEpB,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;IAClC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,IAAO,EACP,UAAoB;IAEpB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,oBAAoB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable handler module.
|
|
3
|
+
*
|
|
4
|
+
* Implements encrypt-by-default semantics: all env vars are encrypted
|
|
5
|
+
* unless they appear on an explicit safe-list AND --allow-plain is used.
|
|
6
|
+
* Even safe-listed keys are encrypted if their value looks like a secret
|
|
7
|
+
* (high entropy or matches a known credential pattern).
|
|
8
|
+
*
|
|
9
|
+
* Secrets are NEVER accepted as CLI arguments — only via interactive
|
|
10
|
+
* prompt (hidden input), stdin pipe, or file descriptor.
|
|
11
|
+
*
|
|
12
|
+
* @module core/env-handler
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Determine whether an environment variable should be encrypted.
|
|
16
|
+
*
|
|
17
|
+
* Default: ALWAYS encrypt. Only safe-listed keys may be stored plain,
|
|
18
|
+
* and even then only if the value does not look sensitive.
|
|
19
|
+
*
|
|
20
|
+
* @param key - The env var key name.
|
|
21
|
+
* @param value - The env var value.
|
|
22
|
+
* @param safeList - Override safe-list (defaults to DEFAULT_SAFE_LIST).
|
|
23
|
+
* @returns `true` if the value should be encrypted.
|
|
24
|
+
*/
|
|
25
|
+
export declare function shouldEncrypt(key: string, value: string, safeList?: readonly string[]): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Calculate Shannon entropy for a string.
|
|
28
|
+
*
|
|
29
|
+
* High-entropy strings (> 4.0 bits/char) are likely API keys, tokens,
|
|
30
|
+
* or other random secrets. Strings shorter than 16 chars are ignored
|
|
31
|
+
* (too short for meaningful entropy measurement).
|
|
32
|
+
*
|
|
33
|
+
* @param value - The string to check.
|
|
34
|
+
* @returns `true` if the string has high entropy (likely a secret).
|
|
35
|
+
*/
|
|
36
|
+
export declare function hasHighEntropy(value: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Check whether a value matches known credential patterns.
|
|
39
|
+
*
|
|
40
|
+
* Detects common service token prefixes, JWTs, PEM keys, and
|
|
41
|
+
* URLs with embedded credentials.
|
|
42
|
+
*
|
|
43
|
+
* @param value - The string to check.
|
|
44
|
+
* @returns `true` if the value matches a known credential pattern.
|
|
45
|
+
*/
|
|
46
|
+
export declare function containsCredentialPattern(value: string): boolean;
|
|
47
|
+
/** Parsed environment variable from a .env file */
|
|
48
|
+
export interface ParsedEnvVar {
|
|
49
|
+
key: string;
|
|
50
|
+
value: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse the content of a `.env` file.
|
|
54
|
+
*
|
|
55
|
+
* Handles:
|
|
56
|
+
* - Standard `KEY=value` pairs
|
|
57
|
+
* - Comments (lines starting with `#`)
|
|
58
|
+
* - Empty lines
|
|
59
|
+
* - Quoted values (single and double)
|
|
60
|
+
* - `export` prefix (`export KEY=value`)
|
|
61
|
+
* - Windows line endings (`\r\n`)
|
|
62
|
+
* - Lines with no `=` (ignored)
|
|
63
|
+
* - Lines with `=` but no value (empty string value)
|
|
64
|
+
* - Duplicate keys (last value wins)
|
|
65
|
+
*
|
|
66
|
+
* @param content - The raw content of a `.env` file.
|
|
67
|
+
* @returns Array of parsed key-value pairs.
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseEnvFile(content: string): ParsedEnvVar[];
|
|
70
|
+
/**
|
|
71
|
+
* Import multiple environment variables for a project.
|
|
72
|
+
*
|
|
73
|
+
* Reads existing env-vars.age (if any), merges in new vars, encrypts
|
|
74
|
+
* and writes back. All vars are encrypted by default — the entire .age
|
|
75
|
+
* file is a single encrypted blob.
|
|
76
|
+
*
|
|
77
|
+
* @param project - The project name.
|
|
78
|
+
* @param vars - Array of key-value pairs to import.
|
|
79
|
+
* @param stateDir - The sync directory path.
|
|
80
|
+
* @param publicKey - The Age public key for encryption.
|
|
81
|
+
* @param privateKey - The Age private key for decryption (to read existing state).
|
|
82
|
+
* @returns The count of imported variables.
|
|
83
|
+
*/
|
|
84
|
+
export declare function importEnvVars(project: string, vars: ParsedEnvVar[], stateDir: string, publicKey: string, privateKey: string): Promise<number>;
|
|
85
|
+
/**
|
|
86
|
+
* Add a single environment variable for a project.
|
|
87
|
+
*
|
|
88
|
+
* @param project - The project name.
|
|
89
|
+
* @param key - The variable key.
|
|
90
|
+
* @param value - The variable value (must come from hidden input / stdin, NEVER CLI args).
|
|
91
|
+
* @param stateDir - The sync directory path.
|
|
92
|
+
* @param publicKey - The Age public key.
|
|
93
|
+
* @param privateKey - The Age private key.
|
|
94
|
+
*/
|
|
95
|
+
export declare function addEnvVar(project: string, key: string, value: string, stateDir: string, publicKey: string, privateKey: string): Promise<void>;
|
|
96
|
+
/** A listed env var (value optionally hidden) */
|
|
97
|
+
export interface ListedEnvVar {
|
|
98
|
+
key: string;
|
|
99
|
+
value: string;
|
|
100
|
+
addedAt: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* List environment variables for a project.
|
|
104
|
+
*
|
|
105
|
+
* @param project - The project name.
|
|
106
|
+
* @param stateDir - The sync directory path.
|
|
107
|
+
* @param privateKey - The Age private key for decryption.
|
|
108
|
+
* @param showValues - If `true`, decrypted values are returned; otherwise masked as '********'.
|
|
109
|
+
* @returns Array of env vars (values hidden by default).
|
|
110
|
+
*/
|
|
111
|
+
export declare function listEnvVars(project: string, stateDir: string, privateKey: string, showValues?: boolean): Promise<ListedEnvVar[]>;
|
|
112
|
+
/**
|
|
113
|
+
* Validate that a key argument does not contain an embedded value.
|
|
114
|
+
*
|
|
115
|
+
* Rejects `KEY=value` syntax in CLI arguments to prevent secrets
|
|
116
|
+
* from appearing in shell history and process lists.
|
|
117
|
+
*
|
|
118
|
+
* @param keyArg - The key argument from the CLI.
|
|
119
|
+
* @throws If the key contains `=` followed by a value.
|
|
120
|
+
*/
|
|
121
|
+
export declare function validateKeyArg(keyArg: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* Read a value from stdin (for piped input).
|
|
124
|
+
*
|
|
125
|
+
* @returns The value read from stdin, trimmed.
|
|
126
|
+
*/
|
|
127
|
+
export declare function readValueFromStdin(): Promise<string>;
|
|
128
|
+
//# sourceMappingURL=env-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-handler.d.ts","sourceRoot":"","sources":["../../src/core/env-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,SAAS,MAAM,EAAsB,GAC9C,OAAO,CAYT;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAkBrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBhE;AAED,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CA8D5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,OAAe,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC,CAazB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgBrD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAuBpD"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable handler module.
|
|
3
|
+
*
|
|
4
|
+
* Implements encrypt-by-default semantics: all env vars are encrypted
|
|
5
|
+
* unless they appear on an explicit safe-list AND --allow-plain is used.
|
|
6
|
+
* Even safe-listed keys are encrypted if their value looks like a secret
|
|
7
|
+
* (high entropy or matches a known credential pattern).
|
|
8
|
+
*
|
|
9
|
+
* Secrets are NEVER accepted as CLI arguments — only via interactive
|
|
10
|
+
* prompt (hidden input), stdin pipe, or file descriptor.
|
|
11
|
+
*
|
|
12
|
+
* @module core/env-handler
|
|
13
|
+
*/
|
|
14
|
+
import { DEFAULT_SAFE_LIST } from '@ctx-sync/shared';
|
|
15
|
+
import { readState, writeState } from './state-manager.js';
|
|
16
|
+
/**
|
|
17
|
+
* Determine whether an environment variable should be encrypted.
|
|
18
|
+
*
|
|
19
|
+
* Default: ALWAYS encrypt. Only safe-listed keys may be stored plain,
|
|
20
|
+
* and even then only if the value does not look sensitive.
|
|
21
|
+
*
|
|
22
|
+
* @param key - The env var key name.
|
|
23
|
+
* @param value - The env var value.
|
|
24
|
+
* @param safeList - Override safe-list (defaults to DEFAULT_SAFE_LIST).
|
|
25
|
+
* @returns `true` if the value should be encrypted.
|
|
26
|
+
*/
|
|
27
|
+
export function shouldEncrypt(key, value, safeList = DEFAULT_SAFE_LIST) {
|
|
28
|
+
// If not on the safe-list → always encrypt
|
|
29
|
+
if (!safeList.includes(key.toUpperCase())) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// On the safe-list, but double-check value doesn't look sensitive
|
|
33
|
+
if (hasHighEntropy(value) || containsCredentialPattern(value)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Calculate Shannon entropy for a string.
|
|
40
|
+
*
|
|
41
|
+
* High-entropy strings (> 4.0 bits/char) are likely API keys, tokens,
|
|
42
|
+
* or other random secrets. Strings shorter than 16 chars are ignored
|
|
43
|
+
* (too short for meaningful entropy measurement).
|
|
44
|
+
*
|
|
45
|
+
* @param value - The string to check.
|
|
46
|
+
* @returns `true` if the string has high entropy (likely a secret).
|
|
47
|
+
*/
|
|
48
|
+
export function hasHighEntropy(value) {
|
|
49
|
+
if (value.length < 16) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const freq = {};
|
|
53
|
+
for (const ch of value) {
|
|
54
|
+
freq[ch] = (freq[ch] ?? 0) + 1;
|
|
55
|
+
}
|
|
56
|
+
const len = value.length;
|
|
57
|
+
let entropy = 0;
|
|
58
|
+
for (const count of Object.values(freq)) {
|
|
59
|
+
const p = count / len;
|
|
60
|
+
entropy -= p * Math.log2(p);
|
|
61
|
+
}
|
|
62
|
+
return entropy > 4.0;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check whether a value matches known credential patterns.
|
|
66
|
+
*
|
|
67
|
+
* Detects common service token prefixes, JWTs, PEM keys, and
|
|
68
|
+
* URLs with embedded credentials.
|
|
69
|
+
*
|
|
70
|
+
* @param value - The string to check.
|
|
71
|
+
* @returns `true` if the value matches a known credential pattern.
|
|
72
|
+
*/
|
|
73
|
+
export function containsCredentialPattern(value) {
|
|
74
|
+
const patterns = [
|
|
75
|
+
/^sk_/, // Stripe
|
|
76
|
+
/^ghp_/, // GitHub PAT
|
|
77
|
+
/^gho_/, // GitHub OAuth
|
|
78
|
+
/^github_pat_/, // GitHub fine-grained PAT
|
|
79
|
+
/^xoxb-/, // Slack bot
|
|
80
|
+
/^xoxp-/, // Slack user
|
|
81
|
+
/^AIza/, // Google API
|
|
82
|
+
/^AKIA/, // AWS Access Key
|
|
83
|
+
/^eyJ[A-Za-z0-9_-]+\./, // JWT tokens
|
|
84
|
+
/-----BEGIN\s+(RSA\s+)?PRIVATE KEY-----/, // PEM private keys
|
|
85
|
+
/-----BEGIN\s+CERTIFICATE-----/, // Certificates
|
|
86
|
+
/:\/\/[^:]*:[^@]+@/, // URLs with embedded credentials
|
|
87
|
+
/^SG\./, // SendGrid
|
|
88
|
+
/^AC[a-f0-9]{32}/, // Twilio
|
|
89
|
+
/^sk-[a-zA-Z0-9]{20,}/, // OpenAI
|
|
90
|
+
];
|
|
91
|
+
return patterns.some((p) => p.test(value));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Parse the content of a `.env` file.
|
|
95
|
+
*
|
|
96
|
+
* Handles:
|
|
97
|
+
* - Standard `KEY=value` pairs
|
|
98
|
+
* - Comments (lines starting with `#`)
|
|
99
|
+
* - Empty lines
|
|
100
|
+
* - Quoted values (single and double)
|
|
101
|
+
* - `export` prefix (`export KEY=value`)
|
|
102
|
+
* - Windows line endings (`\r\n`)
|
|
103
|
+
* - Lines with no `=` (ignored)
|
|
104
|
+
* - Lines with `=` but no value (empty string value)
|
|
105
|
+
* - Duplicate keys (last value wins)
|
|
106
|
+
*
|
|
107
|
+
* @param content - The raw content of a `.env` file.
|
|
108
|
+
* @returns Array of parsed key-value pairs.
|
|
109
|
+
*/
|
|
110
|
+
export function parseEnvFile(content) {
|
|
111
|
+
const result = [];
|
|
112
|
+
const seen = new Map();
|
|
113
|
+
// Strip null bytes and other control characters (except \n, \r, \t)
|
|
114
|
+
// eslint-disable-next-line no-control-regex
|
|
115
|
+
const sanitised = content.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
|
|
116
|
+
// Normalise line endings
|
|
117
|
+
const lines = sanitised.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
118
|
+
for (const rawLine of lines) {
|
|
119
|
+
const line = rawLine.trim();
|
|
120
|
+
// Skip empty lines and comments
|
|
121
|
+
if (!line || line.startsWith('#')) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// Strip optional `export ` prefix
|
|
125
|
+
const stripped = line.startsWith('export ')
|
|
126
|
+
? line.slice('export '.length).trim()
|
|
127
|
+
: line;
|
|
128
|
+
// Must contain `=`
|
|
129
|
+
const eqIndex = stripped.indexOf('=');
|
|
130
|
+
if (eqIndex === -1) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const key = stripped.slice(0, eqIndex).trim();
|
|
134
|
+
if (!key) {
|
|
135
|
+
continue; // Skip lines like `=value`
|
|
136
|
+
}
|
|
137
|
+
let value = stripped.slice(eqIndex + 1).trim();
|
|
138
|
+
// Strip surrounding quotes
|
|
139
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
140
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
141
|
+
value = value.slice(1, -1);
|
|
142
|
+
}
|
|
143
|
+
// Truncate excessively long values (> 1MB) to prevent memory issues
|
|
144
|
+
const MAX_VALUE_LENGTH = 1_048_576; // 1 MB
|
|
145
|
+
if (value.length > MAX_VALUE_LENGTH) {
|
|
146
|
+
value = value.slice(0, MAX_VALUE_LENGTH);
|
|
147
|
+
}
|
|
148
|
+
// Handle duplicate keys — last value wins
|
|
149
|
+
const existingIndex = seen.get(key);
|
|
150
|
+
if (existingIndex !== undefined) {
|
|
151
|
+
result[existingIndex] = { key, value };
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
seen.set(key, result.length);
|
|
155
|
+
result.push({ key, value });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Import multiple environment variables for a project.
|
|
162
|
+
*
|
|
163
|
+
* Reads existing env-vars.age (if any), merges in new vars, encrypts
|
|
164
|
+
* and writes back. All vars are encrypted by default — the entire .age
|
|
165
|
+
* file is a single encrypted blob.
|
|
166
|
+
*
|
|
167
|
+
* @param project - The project name.
|
|
168
|
+
* @param vars - Array of key-value pairs to import.
|
|
169
|
+
* @param stateDir - The sync directory path.
|
|
170
|
+
* @param publicKey - The Age public key for encryption.
|
|
171
|
+
* @param privateKey - The Age private key for decryption (to read existing state).
|
|
172
|
+
* @returns The count of imported variables.
|
|
173
|
+
*/
|
|
174
|
+
export async function importEnvVars(project, vars, stateDir, publicKey, privateKey) {
|
|
175
|
+
// Read existing env vars
|
|
176
|
+
const existing = (await readState(stateDir, privateKey, 'env-vars')) ?? {};
|
|
177
|
+
// Ensure project bucket exists
|
|
178
|
+
const projectBucket = existing[project] ?? {};
|
|
179
|
+
existing[project] = projectBucket;
|
|
180
|
+
const now = new Date().toISOString();
|
|
181
|
+
for (const { key, value } of vars) {
|
|
182
|
+
projectBucket[key] = {
|
|
183
|
+
value,
|
|
184
|
+
addedAt: now,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// Write encrypted state
|
|
188
|
+
await writeState(stateDir, existing, publicKey, 'env-vars');
|
|
189
|
+
return vars.length;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Add a single environment variable for a project.
|
|
193
|
+
*
|
|
194
|
+
* @param project - The project name.
|
|
195
|
+
* @param key - The variable key.
|
|
196
|
+
* @param value - The variable value (must come from hidden input / stdin, NEVER CLI args).
|
|
197
|
+
* @param stateDir - The sync directory path.
|
|
198
|
+
* @param publicKey - The Age public key.
|
|
199
|
+
* @param privateKey - The Age private key.
|
|
200
|
+
*/
|
|
201
|
+
export async function addEnvVar(project, key, value, stateDir, publicKey, privateKey) {
|
|
202
|
+
await importEnvVars(project, [{ key, value }], stateDir, publicKey, privateKey);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* List environment variables for a project.
|
|
206
|
+
*
|
|
207
|
+
* @param project - The project name.
|
|
208
|
+
* @param stateDir - The sync directory path.
|
|
209
|
+
* @param privateKey - The Age private key for decryption.
|
|
210
|
+
* @param showValues - If `true`, decrypted values are returned; otherwise masked as '********'.
|
|
211
|
+
* @returns Array of env vars (values hidden by default).
|
|
212
|
+
*/
|
|
213
|
+
export async function listEnvVars(project, stateDir, privateKey, showValues = false) {
|
|
214
|
+
const envVars = await readState(stateDir, privateKey, 'env-vars');
|
|
215
|
+
if (!envVars || !envVars[project]) {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
const projectVars = envVars[project] ?? {};
|
|
219
|
+
return Object.entries(projectVars).map(([key, entry]) => ({
|
|
220
|
+
key,
|
|
221
|
+
value: showValues ? entry.value : '********',
|
|
222
|
+
addedAt: entry.addedAt,
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Validate that a key argument does not contain an embedded value.
|
|
227
|
+
*
|
|
228
|
+
* Rejects `KEY=value` syntax in CLI arguments to prevent secrets
|
|
229
|
+
* from appearing in shell history and process lists.
|
|
230
|
+
*
|
|
231
|
+
* @param keyArg - The key argument from the CLI.
|
|
232
|
+
* @throws If the key contains `=` followed by a value.
|
|
233
|
+
*/
|
|
234
|
+
export function validateKeyArg(keyArg) {
|
|
235
|
+
if (keyArg.includes('=')) {
|
|
236
|
+
const eqIndex = keyArg.indexOf('=');
|
|
237
|
+
const afterEq = keyArg.slice(eqIndex + 1);
|
|
238
|
+
if (afterEq.length > 0) {
|
|
239
|
+
throw new Error('Cannot pass secret values as CLI arguments.\n' +
|
|
240
|
+
'Secret values are visible in shell history and process lists.\n\n' +
|
|
241
|
+
'Use one of these secure methods instead:\n' +
|
|
242
|
+
` Interactive: ctx-sync env add <project> ${keyArg.slice(0, eqIndex)}\n` +
|
|
243
|
+
` Stdin pipe: echo "value" | ctx-sync env add <project> ${keyArg.slice(0, eqIndex)} --stdin\n` +
|
|
244
|
+
` File desc: ctx-sync env add <project> ${keyArg.slice(0, eqIndex)} --from-fd 3 3< <(pass show key)`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return keyArg;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Read a value from stdin (for piped input).
|
|
251
|
+
*
|
|
252
|
+
* @returns The value read from stdin, trimmed.
|
|
253
|
+
*/
|
|
254
|
+
export function readValueFromStdin() {
|
|
255
|
+
return new Promise((resolve, reject) => {
|
|
256
|
+
let data = '';
|
|
257
|
+
if (process.stdin.isTTY) {
|
|
258
|
+
reject(new Error('No data piped to stdin.\n' +
|
|
259
|
+
'Usage: echo "value" | ctx-sync env add <project> <key> --stdin'));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
process.stdin.setEncoding('utf-8');
|
|
263
|
+
process.stdin.on('data', (chunk) => {
|
|
264
|
+
data += chunk;
|
|
265
|
+
});
|
|
266
|
+
process.stdin.on('end', () => {
|
|
267
|
+
resolve(data.trim());
|
|
268
|
+
});
|
|
269
|
+
process.stdin.on('error', reject);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=env-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-handler.js","sourceRoot":"","sources":["../../src/core/env-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git sync engine module.
|
|
3
|
+
*
|
|
4
|
+
* Manages the local Git repository used for syncing encrypted state
|
|
5
|
+
* files between machines. Provides operations for init, commit, push,
|
|
6
|
+
* pull, and status queries. All remote operations validate transport
|
|
7
|
+
* security before proceeding.
|
|
8
|
+
*
|
|
9
|
+
* @module core/git-sync
|
|
10
|
+
*/
|
|
11
|
+
/** Result of a getStatus() call */
|
|
12
|
+
export interface SyncStatus {
|
|
13
|
+
/** List of changed/untracked files */
|
|
14
|
+
files: string[];
|
|
15
|
+
/** Number of commits ahead of remote */
|
|
16
|
+
ahead: number;
|
|
17
|
+
/** Number of commits behind remote */
|
|
18
|
+
behind: number;
|
|
19
|
+
/** Whether the working tree is clean */
|
|
20
|
+
isClean: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Initialise a Git repository in the given directory.
|
|
24
|
+
*
|
|
25
|
+
* If the directory already contains a `.git/` folder, this is a no-op.
|
|
26
|
+
* Otherwise, a new Git repository is created with `git init`.
|
|
27
|
+
*
|
|
28
|
+
* @param dir - The directory to initialise.
|
|
29
|
+
* @returns `true` if a new repo was created, `false` if one already existed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function initRepo(dir: string): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Add a remote to the Git repository.
|
|
34
|
+
*
|
|
35
|
+
* Validates the URL using transport security before adding.
|
|
36
|
+
* If a remote with the given name already exists, it is updated.
|
|
37
|
+
*
|
|
38
|
+
* @param dir - The Git repository directory.
|
|
39
|
+
* @param url - The remote URL (SSH or HTTPS).
|
|
40
|
+
* @param remoteName - The name for the remote (default: 'origin').
|
|
41
|
+
* @throws If the URL uses an insecure protocol.
|
|
42
|
+
*/
|
|
43
|
+
export declare function addRemote(dir: string, url: string, remoteName?: string): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Stage files and commit changes to the sync repository.
|
|
46
|
+
*
|
|
47
|
+
* Stages the specified files, then commits with the given message.
|
|
48
|
+
* If there are no changes to commit (working tree is clean after staging),
|
|
49
|
+
* the commit is skipped and `null` is returned.
|
|
50
|
+
*
|
|
51
|
+
* @param dir - The Git repository directory.
|
|
52
|
+
* @param files - List of file paths (relative to dir) to stage.
|
|
53
|
+
* @param message - The commit message.
|
|
54
|
+
* @returns The commit hash, or `null` if no changes were committed.
|
|
55
|
+
*/
|
|
56
|
+
export declare function commitState(dir: string, files: string[], message: string): Promise<string | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Push the local sync repository to the remote.
|
|
59
|
+
*
|
|
60
|
+
* Validates the remote URL before pushing. If no remote is configured,
|
|
61
|
+
* the push is skipped.
|
|
62
|
+
*
|
|
63
|
+
* @param dir - The Git repository directory.
|
|
64
|
+
* @param remoteName - The remote name (default: 'origin').
|
|
65
|
+
* @param branch - The branch to push (default: 'main').
|
|
66
|
+
* @throws If the remote URL uses an insecure protocol.
|
|
67
|
+
*/
|
|
68
|
+
export declare function pushState(dir: string, remoteName?: string, branch?: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Pull the latest changes from the remote sync repository.
|
|
71
|
+
*
|
|
72
|
+
* Validates the remote URL before pulling. If no remote is configured,
|
|
73
|
+
* the pull is skipped.
|
|
74
|
+
*
|
|
75
|
+
* @param dir - The Git repository directory.
|
|
76
|
+
* @param remoteName - The remote name (default: 'origin').
|
|
77
|
+
* @param branch - The branch to pull (default: 'main').
|
|
78
|
+
* @throws If the remote URL uses an insecure protocol.
|
|
79
|
+
*/
|
|
80
|
+
export declare function pullState(dir: string, remoteName?: string, branch?: string): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Get the current sync status of the repository.
|
|
83
|
+
*
|
|
84
|
+
* @param dir - The Git repository directory.
|
|
85
|
+
* @returns An object describing changed files, ahead/behind counts, and cleanliness.
|
|
86
|
+
*/
|
|
87
|
+
export declare function getStatus(dir: string): Promise<SyncStatus>;
|
|
88
|
+
//# sourceMappingURL=git-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-sync.d.ts","sourceRoot":"","sources":["../../src/core/git-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,mCAAmC;AACnC,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAY5D;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAiB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAiB,EAC7B,MAAM,GAAE,MAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAiB,EAC7B,MAAM,GAAE,MAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAUhE"}
|