microgit-cli 0.0.0-stage → 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/README.md +257 -2
- package/package.json +26 -4
- package/src/api/index.js +111 -0
- package/src/api/index.js.map +1 -0
- package/src/cli/index.js +58 -0
- package/src/cli/index.js.map +1 -0
- package/src/cli/repl.js +312 -0
- package/src/cli/repl.js.map +1 -0
- package/src/commands/auth.js +305 -0
- package/src/commands/auth.js.map +1 -0
- package/src/commands/config.js +106 -0
- package/src/commands/config.js.map +1 -0
- package/src/commands/git.js +159 -0
- package/src/commands/git.js.map +1 -0
- package/src/commands/registry.js +139 -0
- package/src/commands/registry.js.map +1 -0
- package/src/commands/repos.js +256 -0
- package/src/commands/repos.js.map +1 -0
- package/src/config/index.js +124 -0
- package/src/config/index.js.map +1 -0
- package/src/config/settings.js +34 -0
- package/src/config/settings.js.map +1 -0
- package/src/error/index.js +14 -0
- package/src/error/index.js.map +1 -0
- package/src/git/index.js +163 -0
- package/src/git/index.js.map +1 -0
- package/src/help/index.js +52 -0
- package/src/help/index.js.map +1 -0
- package/src/index.js +9 -0
- package/src/index.js.map +1 -0
- package/src/theme/banner.js +44 -0
- package/src/theme/banner.js.map +1 -0
- package/src/theme/index.js +5 -0
- package/src/theme/index.js.map +1 -0
- package/src/theme/logo.js +36 -0
- package/src/theme/logo.js.map +1 -0
- package/src/theme/palette.js +32 -0
- package/src/theme/palette.js.map +1 -0
- package/src/ui/index.js +104 -0
- package/src/ui/index.js.map +1 -0
- package/src/util/index.js +30 -0
- package/src/util/index.js.map +1 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import { ApiClient } from '../api/index.js';
|
|
3
|
+
import { loadConfig, requireAuth } from '../config/index.js';
|
|
4
|
+
import { cloneRepo, initRepo } from '../git/index.js';
|
|
5
|
+
import { BIN } from '../config/settings.js';
|
|
6
|
+
import { ask, confirm, cyan, dim, fail, faint, info, ok, printJson, table } from '../ui/index.js';
|
|
7
|
+
import { parseTarget, positiveInt, text, trimmed } from '../util/index.js';
|
|
8
|
+
/** Ask public/private when the flag is not given; Enter means public. */
|
|
9
|
+
async function askVisibility() {
|
|
10
|
+
const answer = (await ask(`Visibility ${dim('[public/private]')} ${faint('(public)')}: `))
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.trim();
|
|
13
|
+
if (answer === '')
|
|
14
|
+
return false;
|
|
15
|
+
if (answer === 'private' || answer === 'priv' || answer === 'p')
|
|
16
|
+
return true;
|
|
17
|
+
if (answer === 'public' || answer === 'pub')
|
|
18
|
+
return false;
|
|
19
|
+
fail(`Unknown visibility '${answer}'. Use public or private.`);
|
|
20
|
+
}
|
|
21
|
+
export async function listCommand(argv) {
|
|
22
|
+
const { values } = parseArgs({
|
|
23
|
+
args: argv,
|
|
24
|
+
allowPositionals: true,
|
|
25
|
+
options: {
|
|
26
|
+
url: { type: 'string' },
|
|
27
|
+
owner: { type: 'string' },
|
|
28
|
+
name: { type: 'string' },
|
|
29
|
+
private: { type: 'boolean' },
|
|
30
|
+
public: { type: 'boolean' },
|
|
31
|
+
page: { type: 'string' },
|
|
32
|
+
limit: { type: 'string' },
|
|
33
|
+
json: { type: 'boolean' },
|
|
34
|
+
help: { type: 'boolean', short: 'h' },
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
if (values.help) {
|
|
38
|
+
console.log(`Usage: ${BIN} list [options]
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
--owner <name> Only repositories owned by this account
|
|
42
|
+
--name <name> Filter by repository name
|
|
43
|
+
--private Only private repositories
|
|
44
|
+
--public Only public repositories
|
|
45
|
+
--page <n> Page number (default 1)
|
|
46
|
+
--limit <n> Page size (default 50, max 200)
|
|
47
|
+
--json Print the raw API response
|
|
48
|
+
--url <url> Server URL
|
|
49
|
+
-h, --help Show this help`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (values.private && values.public)
|
|
53
|
+
fail('Use either --private or --public, not both.');
|
|
54
|
+
const visibility = values.private ? true : values.public ? false : null;
|
|
55
|
+
const config = loadConfig(text(values.url));
|
|
56
|
+
const api = new ApiClient(config.url, config.token);
|
|
57
|
+
const result = await api.listRepos({
|
|
58
|
+
owner: trimmed(values.owner),
|
|
59
|
+
name: trimmed(values.name),
|
|
60
|
+
private: visibility,
|
|
61
|
+
page: positiveInt(values.page, '--page', 1),
|
|
62
|
+
limit: positiveInt(values.limit, '--limit', 50),
|
|
63
|
+
});
|
|
64
|
+
if (values.json) {
|
|
65
|
+
printJson(result);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (result.repositories.length === 0) {
|
|
69
|
+
info('No repositories found.');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const rows = result.repositories.map((repo) => [
|
|
73
|
+
`${repo.owner}/${repo.name}`,
|
|
74
|
+
repo.private ? 'private' : 'public',
|
|
75
|
+
repo.cloneUrl,
|
|
76
|
+
]);
|
|
77
|
+
console.log(table(['REPOSITORY', 'VISIBILITY', 'CLONE URL'], rows));
|
|
78
|
+
info(`${result.total} ${result.total === 1 ? 'repository' : 'repositories'} · ` +
|
|
79
|
+
`page ${result.page}/${Math.max(result.pages, 1)}`);
|
|
80
|
+
}
|
|
81
|
+
export async function newCommand(argv) {
|
|
82
|
+
const { values, positionals } = parseArgs({
|
|
83
|
+
args: argv,
|
|
84
|
+
allowPositionals: true,
|
|
85
|
+
options: {
|
|
86
|
+
url: { type: 'string' },
|
|
87
|
+
private: { type: 'boolean' },
|
|
88
|
+
public: { type: 'boolean' },
|
|
89
|
+
clone: { type: 'boolean' },
|
|
90
|
+
init: { type: 'boolean' },
|
|
91
|
+
dir: { type: 'string' },
|
|
92
|
+
json: { type: 'boolean' },
|
|
93
|
+
help: { type: 'boolean', short: 'h' },
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
if (values.help) {
|
|
97
|
+
console.log(`Usage: ${BIN} new <name> [options]
|
|
98
|
+
|
|
99
|
+
Create a repository for the signed-in account. Without --private or --public
|
|
100
|
+
the CLI asks for the visibility (Enter creates a public repository).
|
|
101
|
+
|
|
102
|
+
Options:
|
|
103
|
+
--private Create it private
|
|
104
|
+
--public Create it public
|
|
105
|
+
--clone Clone it into ./<name> (or --dir) afterwards
|
|
106
|
+
--init Create ./<name>, make a README, first commit and push
|
|
107
|
+
--dir <path> Directory for --clone/--init (default: repository name)
|
|
108
|
+
--json Print the API response as JSON
|
|
109
|
+
--url <url> Server URL
|
|
110
|
+
-h, --help Show this help
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
${BIN} new app
|
|
114
|
+
${BIN} new secret --private
|
|
115
|
+
${BIN} new app --init`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const name = trimmed(positionals[0]);
|
|
119
|
+
if (!name)
|
|
120
|
+
fail(`Repository name is required. Usage: ${BIN} new <name>`);
|
|
121
|
+
if (values.clone && values.init)
|
|
122
|
+
fail('Use either --clone or --init, not both.');
|
|
123
|
+
if (values.private && values.public)
|
|
124
|
+
fail('Use either --private or --public, not both.');
|
|
125
|
+
if (values.json && (values.clone || values.init)) {
|
|
126
|
+
fail('--json cannot be combined with --clone or --init.');
|
|
127
|
+
}
|
|
128
|
+
let isPrivate;
|
|
129
|
+
if (values.private)
|
|
130
|
+
isPrivate = true;
|
|
131
|
+
else if (values.public)
|
|
132
|
+
isPrivate = false;
|
|
133
|
+
else if (values.json || !process.stdin.isTTY)
|
|
134
|
+
isPrivate = false;
|
|
135
|
+
else
|
|
136
|
+
isPrivate = await askVisibility();
|
|
137
|
+
const config = loadConfig(text(values.url));
|
|
138
|
+
requireAuth(config);
|
|
139
|
+
const api = new ApiClient(config.url, config.token);
|
|
140
|
+
const repo = await api.newRepo(name, isPrivate);
|
|
141
|
+
if (values.json) {
|
|
142
|
+
printJson({ ...repo, url: config.url });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (repo.created)
|
|
146
|
+
ok(`Created ${repo.owner}/${repo.name}${repo.private ? ' (private)' : ''}`);
|
|
147
|
+
else
|
|
148
|
+
info(`Already exists: ${repo.owner}/${repo.name}`);
|
|
149
|
+
info(`Clone URL: ${repo.cloneUrl}`);
|
|
150
|
+
const dir = trimmed(values.dir) ?? repo.name;
|
|
151
|
+
if (values.clone) {
|
|
152
|
+
await cloneRepo(repo, dir, config);
|
|
153
|
+
ok(`Cloned into ${dir}`);
|
|
154
|
+
info(`cd ${dir}`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (values.init) {
|
|
158
|
+
await initRepo(repo, dir, config);
|
|
159
|
+
ok(`Created ${dir}, made the first commit and pushed it`);
|
|
160
|
+
info(`cd ${dir}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
info(`Next: ${cyan(`${BIN} clone ${repo.owner}/${repo.name}`)}`);
|
|
164
|
+
}
|
|
165
|
+
export async function deleteCommand(argv) {
|
|
166
|
+
const { values, positionals } = parseArgs({
|
|
167
|
+
args: argv,
|
|
168
|
+
allowPositionals: true,
|
|
169
|
+
options: {
|
|
170
|
+
url: { type: 'string' },
|
|
171
|
+
yes: { type: 'boolean', short: 'y' },
|
|
172
|
+
json: { type: 'boolean' },
|
|
173
|
+
help: { type: 'boolean', short: 'h' },
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
if (values.help) {
|
|
177
|
+
console.log(`Usage: ${BIN} delete <name|owner/name> [options]
|
|
178
|
+
|
|
179
|
+
Options:
|
|
180
|
+
-y, --yes Do not ask for confirmation
|
|
181
|
+
--json Print the API response as JSON
|
|
182
|
+
--url <url> Server URL
|
|
183
|
+
-h, --help Show this help`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const target = trimmed(positionals[0]);
|
|
187
|
+
if (!target)
|
|
188
|
+
fail(`Repository name is required. Usage: ${BIN} delete <name|owner/name>`);
|
|
189
|
+
const parsed = parseTarget(target);
|
|
190
|
+
const config = loadConfig(text(values.url));
|
|
191
|
+
requireAuth(config);
|
|
192
|
+
const owner = parsed.owner ?? config.username;
|
|
193
|
+
if (!owner)
|
|
194
|
+
fail('No owner given and no username stored. Use <owner/name> or log in first.');
|
|
195
|
+
if (!values.yes) {
|
|
196
|
+
const approved = await confirm(`Delete ${owner}/${parsed.name}? This cannot be undone.`, false);
|
|
197
|
+
if (!approved) {
|
|
198
|
+
info('Cancelled.');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const api = new ApiClient(config.url, config.token);
|
|
203
|
+
const result = await api.deleteRepo(owner, parsed.name);
|
|
204
|
+
if (values.json) {
|
|
205
|
+
printJson(result);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
ok(`Deleted ${result.owner}/${result.name}`);
|
|
209
|
+
}
|
|
210
|
+
export async function cloneCommand(argv) {
|
|
211
|
+
const { values, positionals } = parseArgs({
|
|
212
|
+
args: argv,
|
|
213
|
+
allowPositionals: true,
|
|
214
|
+
options: {
|
|
215
|
+
url: { type: 'string' },
|
|
216
|
+
dir: { type: 'string' },
|
|
217
|
+
json: { type: 'boolean' },
|
|
218
|
+
help: { type: 'boolean', short: 'h' },
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
if (values.help) {
|
|
222
|
+
console.log(`Usage: ${BIN} clone <name|owner/name> [directory] [options]
|
|
223
|
+
|
|
224
|
+
Clone a repository using the URL returned by the server.
|
|
225
|
+
|
|
226
|
+
Options:
|
|
227
|
+
--dir <path> Target directory (default: repository name)
|
|
228
|
+
--json Print repository details as JSON after cloning
|
|
229
|
+
--url <url> Server URL
|
|
230
|
+
-h, --help Show this help
|
|
231
|
+
|
|
232
|
+
Examples:
|
|
233
|
+
${BIN} clone app
|
|
234
|
+
${BIN} clone alice/app ./checkout`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const target = trimmed(positionals[0]);
|
|
238
|
+
if (!target)
|
|
239
|
+
fail(`Repository is required. Usage: ${BIN} clone <name|owner/name>`);
|
|
240
|
+
const parsed = parseTarget(target);
|
|
241
|
+
const config = loadConfig(text(values.url));
|
|
242
|
+
const owner = parsed.owner ?? config.username;
|
|
243
|
+
if (!owner)
|
|
244
|
+
fail('No owner given and no username stored. Use <owner/name> or log in first.');
|
|
245
|
+
const api = new ApiClient(config.url, config.token);
|
|
246
|
+
const repo = await api.repo(owner, parsed.name);
|
|
247
|
+
const dir = trimmed(values.dir) ?? trimmed(positionals[1]) ?? repo.name;
|
|
248
|
+
await cloneRepo(repo, dir, config);
|
|
249
|
+
if (values.json) {
|
|
250
|
+
printJson({ ...repo, directory: dir });
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
ok(`Cloned ${repo.owner}/${repo.name} into ${dir}`);
|
|
254
|
+
info(`cd ${dir}`);
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=repos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repos.js","sourceRoot":"","sources":["../../src/commands/repos.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { CliError } from '../error/index.js';
|
|
5
|
+
import { BIN, SETTINGS } from './settings.js';
|
|
6
|
+
/** Environment variables, named from the settings prefix. */
|
|
7
|
+
export const ENV = {
|
|
8
|
+
url: `${SETTINGS.envPrefix}_URL`,
|
|
9
|
+
token: `${SETTINGS.envPrefix}_TOKEN`,
|
|
10
|
+
username: `${SETTINGS.envPrefix}_USERNAME`,
|
|
11
|
+
password: `${SETTINGS.envPrefix}_PASSWORD`,
|
|
12
|
+
config: `${SETTINGS.envPrefix}_CONFIG`,
|
|
13
|
+
};
|
|
14
|
+
export const DEFAULT_URL = 'http://127.0.0.1:3000';
|
|
15
|
+
/** `MICROGIT_CONFIG` (or the settings prefix) can point at a different file. */
|
|
16
|
+
export function configFilePath() {
|
|
17
|
+
const override = process.env[ENV.config]?.trim();
|
|
18
|
+
return override
|
|
19
|
+
? path.resolve(override)
|
|
20
|
+
: path.join(os.homedir(), SETTINGS.configDir, 'config.json');
|
|
21
|
+
}
|
|
22
|
+
export function readStoredConfig() {
|
|
23
|
+
const file = configFilePath();
|
|
24
|
+
let raw;
|
|
25
|
+
try {
|
|
26
|
+
raw = fs.readFileSync(file, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error.code === 'ENOENT')
|
|
30
|
+
return {};
|
|
31
|
+
throw new CliError(`Cannot read the config file ${file}: ${error.message}`);
|
|
32
|
+
}
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = JSON.parse(raw);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new CliError(`Config file ${file} is not valid JSON: ${error.message}\n` +
|
|
39
|
+
`Fix it or run \`${BIN} config clear\`.`);
|
|
40
|
+
}
|
|
41
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
42
|
+
throw new CliError(`Config file ${file} must contain a JSON object.`);
|
|
43
|
+
}
|
|
44
|
+
const value = parsed;
|
|
45
|
+
const text = (key) => typeof value[key] === 'string' && value[key] !== '' ? value[key] : undefined;
|
|
46
|
+
return {
|
|
47
|
+
url: text('url'),
|
|
48
|
+
username: text('username'),
|
|
49
|
+
token: text('token'),
|
|
50
|
+
expiresAt: text('expiresAt'),
|
|
51
|
+
password: text('password'),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function writeStoredConfig(config) {
|
|
55
|
+
const file = configFilePath();
|
|
56
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
57
|
+
const clean = {};
|
|
58
|
+
if (config.url)
|
|
59
|
+
clean.url = config.url;
|
|
60
|
+
if (config.username)
|
|
61
|
+
clean.username = config.username;
|
|
62
|
+
if (config.token)
|
|
63
|
+
clean.token = config.token;
|
|
64
|
+
if (config.expiresAt)
|
|
65
|
+
clean.expiresAt = config.expiresAt;
|
|
66
|
+
if (config.password)
|
|
67
|
+
clean.password = config.password;
|
|
68
|
+
fs.writeFileSync(file, `${JSON.stringify(clean, null, 2)}\n`, {
|
|
69
|
+
encoding: 'utf8',
|
|
70
|
+
mode: 0o600,
|
|
71
|
+
});
|
|
72
|
+
// Windows ignores the mode; everywhere else keep the file private.
|
|
73
|
+
try {
|
|
74
|
+
fs.chmodSync(file, 0o600);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Best effort only.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export function clearStoredConfig() {
|
|
81
|
+
try {
|
|
82
|
+
fs.rmSync(configFilePath(), { force: true });
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw new CliError(`Cannot remove the config file: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Accept `host`, `host:3000`, `https://host`; strip trailing slashes. */
|
|
89
|
+
export function normalizeUrl(raw) {
|
|
90
|
+
const trimmed = raw.trim().replace(/\/+$/, '');
|
|
91
|
+
if (trimmed === '')
|
|
92
|
+
return DEFAULT_URL;
|
|
93
|
+
return /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `http://${trimmed}`;
|
|
94
|
+
}
|
|
95
|
+
/** `--url` first, then the URL env var, then the stored value, then localhost. */
|
|
96
|
+
export function resolveUrl(flagUrl) {
|
|
97
|
+
const stored = readStoredConfig();
|
|
98
|
+
return normalizeUrl(flagUrl?.trim() || process.env[ENV.url]?.trim() || stored.url || DEFAULT_URL);
|
|
99
|
+
}
|
|
100
|
+
/** Everything a command needs: URL, token and optional git credentials. */
|
|
101
|
+
export function loadConfig(flagUrl) {
|
|
102
|
+
const stored = readStoredConfig();
|
|
103
|
+
return {
|
|
104
|
+
...stored,
|
|
105
|
+
url: resolveUrl(flagUrl),
|
|
106
|
+
token: process.env[ENV.token]?.trim() || stored.token,
|
|
107
|
+
username: process.env[ENV.username]?.trim() || stored.username,
|
|
108
|
+
password: process.env[ENV.password] || stored.password,
|
|
109
|
+
configPath: configFilePath(),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function requireAuth(config) {
|
|
113
|
+
if (!config.token) {
|
|
114
|
+
throw new CliError(`You are not signed in. Run \`${BIN} login\` first.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Basic credentials for git, or `null` when only the API session is stored. */
|
|
118
|
+
export function gitAuth(config) {
|
|
119
|
+
if (config.username && config.password) {
|
|
120
|
+
return { username: config.username, password: config.password };
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every static, user-facing name in one place. Change these and the banners,
|
|
3
|
+
* prompt, help page, messages, environment variables and config folder follow.
|
|
4
|
+
*
|
|
5
|
+
* Alternative taglines you can drop in:
|
|
6
|
+
* 'self-hosted git over ssh & http, in one command'
|
|
7
|
+
* 'host, clone and push — no cloud in the middle'
|
|
8
|
+
* Alternative labels: 'gitserver', 'githost', 'git service'.
|
|
9
|
+
*/
|
|
10
|
+
export const SETTINGS = {
|
|
11
|
+
/** npm package name (`name` in package.json). */
|
|
12
|
+
packageName: 'microgit-cli',
|
|
13
|
+
/** Command installed on PATH (`bin` in package.json). */
|
|
14
|
+
bin: 'microgit',
|
|
15
|
+
/** Product name shown in banners and help. */
|
|
16
|
+
name: 'microgit-cli',
|
|
17
|
+
/** Wordmark at the start of the ASCII logo: `μgit ━━━ ●`. */
|
|
18
|
+
mark: 'μgit',
|
|
19
|
+
version: '0.1.0',
|
|
20
|
+
/** Short label printed next to the name: `microgit-cli • <label>`. */
|
|
21
|
+
label: 'git client',
|
|
22
|
+
tagline: 'manage your own git server — clone, push, host',
|
|
23
|
+
/** One-line description for package.json and README. */
|
|
24
|
+
description: 'Command line client for a self-hosted git server',
|
|
25
|
+
/** Footer line in the boxed logo and the one-line version. */
|
|
26
|
+
footer: 'zero deps · Node 21+',
|
|
27
|
+
/** Prefix for environment variables (MICROGIT_URL, MICROGIT_TOKEN, ...). */
|
|
28
|
+
envPrefix: 'MICROGIT',
|
|
29
|
+
/** Folder under the home directory holding config.json and history. */
|
|
30
|
+
configDir: '.microgit',
|
|
31
|
+
};
|
|
32
|
+
/** The command users type. */
|
|
33
|
+
export const BIN = SETTINGS.bin;
|
|
34
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/config/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,iDAAiD;IACjD,WAAW,EAAE,cAAc;IAC3B,yDAAyD;IACzD,GAAG,EAAE,UAAU;IACf,8CAA8C;IAC9C,IAAI,EAAE,cAAc;IACpB,6DAA6D;IAC7D,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,OAAO;IAChB,sEAAsE;IACtE,KAAK,EAAE,YAAY;IACnB,OAAO,EAAE,gDAAgD;IACzD,wDAAwD;IACxD,WAAW,EAAE,kDAAkD;IAC/D,8DAA8D;IAC9D,MAAM,EAAE,sBAAsB;IAE9B,4EAA4E;IAC5E,SAAS,EAAE,UAAU;IACrB,uEAAuE;IACvE,SAAS,EAAE,WAAW;CACd,CAAC;AAEX,8BAA8B;AAC9B,MAAM,CAAC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** An expected failure: printed without a stack trace and the process exits 1. */
|
|
2
|
+
export class CliError extends Error {
|
|
3
|
+
exitCode;
|
|
4
|
+
constructor(message, exitCode = 1) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'CliError';
|
|
7
|
+
this.exitCode = exitCode;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/** Readable text for anything thrown, used by the CLI and the shell. */
|
|
11
|
+
export function errorMessage(error) {
|
|
12
|
+
return error instanceof Error ? error.message : String(error);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/error/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,QAAQ,CAAS;IAE1B,YAAY,OAAe,EAAE,QAAQ,GAAG,CAAC;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
package/src/git/index.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ENV, gitAuth } from '../config/index.js';
|
|
5
|
+
import { CliError } from '../error/index.js';
|
|
6
|
+
import { BIN } from '../config/settings.js';
|
|
7
|
+
import { info } from '../ui/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Git talks HTTP Basic, not the API's Bearer token. Pass the credentials as an
|
|
10
|
+
* `http.extraHeader` through the environment: unlike a URL or a remote entry it
|
|
11
|
+
* is not written to `.git/config`, and unlike `-c` it is not in the process
|
|
12
|
+
* arguments on most systems.
|
|
13
|
+
*/
|
|
14
|
+
function gitEnv(auth) {
|
|
15
|
+
const env = { ...process.env };
|
|
16
|
+
delete env.GIT_CONFIG_COUNT;
|
|
17
|
+
delete env.GIT_CONFIG_KEY_0;
|
|
18
|
+
delete env.GIT_CONFIG_VALUE_0;
|
|
19
|
+
if (!process.stdin.isTTY)
|
|
20
|
+
env.GIT_TERMINAL_PROMPT = '0';
|
|
21
|
+
if (auth) {
|
|
22
|
+
const basic = Buffer.from(`${auth.username}:${auth.password}`, 'utf8').toString('base64');
|
|
23
|
+
env.GIT_CONFIG_COUNT = '1';
|
|
24
|
+
env.GIT_CONFIG_KEY_0 = 'http.extraHeader';
|
|
25
|
+
env.GIT_CONFIG_VALUE_0 = `Authorization: Basic ${basic}`;
|
|
26
|
+
}
|
|
27
|
+
return env;
|
|
28
|
+
}
|
|
29
|
+
/** Run git, optionally with Basic auth and with inherited stdout/stderr. */
|
|
30
|
+
export function git(args, options = {}) {
|
|
31
|
+
const cwd = options.cwd ?? process.cwd();
|
|
32
|
+
const inherit = options.inherit ?? false;
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const child = spawn('git', args, {
|
|
35
|
+
cwd,
|
|
36
|
+
env: gitEnv(options.auth ?? null),
|
|
37
|
+
stdio: inherit ? 'inherit' : ['ignore', 'pipe', 'pipe'],
|
|
38
|
+
windowsHide: true,
|
|
39
|
+
});
|
|
40
|
+
let stdout = '';
|
|
41
|
+
let stderr = '';
|
|
42
|
+
if (!inherit) {
|
|
43
|
+
child.stdout?.on('data', (chunk) => {
|
|
44
|
+
stdout += chunk.toString('utf8');
|
|
45
|
+
});
|
|
46
|
+
child.stderr?.on('data', (chunk) => {
|
|
47
|
+
stderr += chunk.toString('utf8');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
child.on('error', (error) => {
|
|
51
|
+
reject(new CliError(`Cannot run git: ${error.message}. Is git installed and in PATH?`));
|
|
52
|
+
});
|
|
53
|
+
child.on('close', (code) => {
|
|
54
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** Run a git command and fail the CLI if it exits non-zero. */
|
|
59
|
+
export async function gitChecked(args, options = {}) {
|
|
60
|
+
const inherit = options.inherit ?? true;
|
|
61
|
+
const result = await git(args, { ...options, inherit });
|
|
62
|
+
if (result.code !== 0) {
|
|
63
|
+
throw new CliError(`git ${args.join(' ')} failed with exit code ${result.code}.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Run a git command and return its trimmed stdout. */
|
|
67
|
+
export async function gitCapture(args, cwd = process.cwd()) {
|
|
68
|
+
const result = await git(args, { cwd });
|
|
69
|
+
if (result.code !== 0) {
|
|
70
|
+
throw new CliError(result.stderr.trim() || `git ${args.join(' ')} failed.`);
|
|
71
|
+
}
|
|
72
|
+
return result.stdout.trim();
|
|
73
|
+
}
|
|
74
|
+
export async function isGitRepo(cwd = process.cwd()) {
|
|
75
|
+
const result = await git(['rev-parse', '--is-inside-work-tree'], { cwd });
|
|
76
|
+
return result.code === 0 && result.stdout.trim() === 'true';
|
|
77
|
+
}
|
|
78
|
+
export async function requireGitRepo(cwd = process.cwd()) {
|
|
79
|
+
if (!(await isGitRepo(cwd))) {
|
|
80
|
+
throw new CliError(`Not a git repository: ${cwd}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function hasRemote(name = 'origin', cwd = process.cwd()) {
|
|
84
|
+
const result = await git(['remote', 'get-url', name], { cwd });
|
|
85
|
+
return result.code === 0;
|
|
86
|
+
}
|
|
87
|
+
async function hasUpstream(cwd = process.cwd()) {
|
|
88
|
+
const result = await git(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], { cwd });
|
|
89
|
+
return result.code === 0;
|
|
90
|
+
}
|
|
91
|
+
/** Give a new repository a local identity when git has none configured. */
|
|
92
|
+
export async function ensureIdentity(cwd, username) {
|
|
93
|
+
const name = await git(['config', '--get', 'user.name'], { cwd });
|
|
94
|
+
const email = await git(['config', '--get', 'user.email'], { cwd });
|
|
95
|
+
if (name.stdout.trim() === '') {
|
|
96
|
+
await gitChecked(['config', 'user.name', username], { cwd, inherit: false });
|
|
97
|
+
info(`Set local git user.name to ${username}`);
|
|
98
|
+
}
|
|
99
|
+
if (email.stdout.trim() === '') {
|
|
100
|
+
await gitChecked(['config', 'user.email', `${username}@${BIN}.local`], { cwd, inherit: false });
|
|
101
|
+
info(`Set local git user.email to ${username}@${BIN}.local`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Clone a repository, using saved credentials when there are any. */
|
|
105
|
+
export async function cloneRepo(repo, dir, config) {
|
|
106
|
+
const target = path.resolve(dir);
|
|
107
|
+
if (fs.existsSync(target)) {
|
|
108
|
+
const entries = fs.readdirSync(target);
|
|
109
|
+
if (entries.length > 0) {
|
|
110
|
+
throw new CliError(`Directory is not empty: ${target}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const auth = gitAuth(config);
|
|
114
|
+
if (repo.private && !auth && !process.stdin.isTTY) {
|
|
115
|
+
throw new CliError('This repository is private and no password is saved. ' +
|
|
116
|
+
`Run \`${BIN} login --save-password\` or set ${ENV.password}.`);
|
|
117
|
+
}
|
|
118
|
+
await gitChecked(['clone', repo.cloneUrl, dir], { auth });
|
|
119
|
+
}
|
|
120
|
+
/** Create a local repository with a first commit and push it to the server. */
|
|
121
|
+
export async function initRepo(repo, dir, config) {
|
|
122
|
+
const target = path.resolve(dir);
|
|
123
|
+
if (fs.existsSync(target) && fs.readdirSync(target).length > 0) {
|
|
124
|
+
throw new CliError(`Directory is not empty: ${target}`);
|
|
125
|
+
}
|
|
126
|
+
fs.mkdirSync(target, { recursive: true });
|
|
127
|
+
await gitChecked(['init', '-b', 'main'], { cwd: target, inherit: false });
|
|
128
|
+
await ensureIdentity(target, config.username ?? BIN);
|
|
129
|
+
const readme = path.join(target, 'README.md');
|
|
130
|
+
if (!fs.existsSync(readme)) {
|
|
131
|
+
fs.writeFileSync(readme, `# ${repo.name}\n`, 'utf8');
|
|
132
|
+
}
|
|
133
|
+
await gitChecked(['add', '-A'], { cwd: target, inherit: false });
|
|
134
|
+
await gitChecked(['commit', '-m', 'Initial commit'], { cwd: target });
|
|
135
|
+
await gitChecked(['remote', 'add', 'origin', repo.cloneUrl], { cwd: target, inherit: false });
|
|
136
|
+
await pushRepo(config, [], target);
|
|
137
|
+
}
|
|
138
|
+
/** Push the current branch, setting an upstream on the first push. */
|
|
139
|
+
export async function pushRepo(config, extra = [], cwd = process.cwd()) {
|
|
140
|
+
await requireGitRepo(cwd);
|
|
141
|
+
if (!(await hasRemote('origin', cwd))) {
|
|
142
|
+
throw new CliError(`This repository has no 'origin' remote. Use \`${BIN} clone <owner/name>\` or add one with git.`);
|
|
143
|
+
}
|
|
144
|
+
const auth = gitAuth(config);
|
|
145
|
+
if (!auth && !process.stdin.isTTY) {
|
|
146
|
+
throw new CliError(`No saved git password. Run \`${BIN} login --save-password\` or set ${ENV.password}.`);
|
|
147
|
+
}
|
|
148
|
+
const args = extra.length > 0
|
|
149
|
+
? ['push', ...extra]
|
|
150
|
+
: (await hasUpstream(cwd))
|
|
151
|
+
? ['push']
|
|
152
|
+
: ['push', '-u', 'origin', 'HEAD'];
|
|
153
|
+
await gitChecked(args, { cwd, auth });
|
|
154
|
+
}
|
|
155
|
+
/** Pull from origin, using saved credentials when there are any. */
|
|
156
|
+
export async function pullRepo(config, extra = [], cwd = process.cwd()) {
|
|
157
|
+
await requireGitRepo(cwd);
|
|
158
|
+
if (!(await hasRemote('origin', cwd))) {
|
|
159
|
+
throw new CliError(`This repository has no 'origin' remote. Use \`${BIN} clone <owner/name>\` or add one with git.`);
|
|
160
|
+
}
|
|
161
|
+
await gitChecked(['pull', ...extra], { cwd, auth: gitAuth(config) });
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAkB,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAmBtC;;;;;GAKG;AACH,SAAS,MAAM,CAAC,IAAoB;IAClC,MAAM,GAAG,GAAsB,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAClD,OAAO,GAAG,CAAC,gBAAgB,CAAC;IAC5B,OAAO,GAAG,CAAC,gBAAgB,CAAC;IAC5B,OAAO,GAAG,CAAC,kBAAkB,CAAC;IAE9B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;QAAE,GAAG,CAAC,mBAAmB,GAAG,GAAG,CAAC;IAExD,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1F,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC;QAC3B,GAAG,CAAC,gBAAgB,GAAG,kBAAkB,CAAC;QAC1C,GAAG,CAAC,kBAAkB,GAAG,wBAAwB,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,GAAG,CAAC,IAAc,EAAE,OAAO,GAAe,EAAE;IAC1D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;IAEzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE;YAC/B,GAAG;YACH,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;YACjC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACvD,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1B,MAAM,CAAC,IAAI,QAAQ,CAAC,mBAAmB,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC,CAAC;QAC1F,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAc,EAAE,OAAO,GAAe,EAAE;IACvE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACxD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,QAAQ,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,0BAA0B,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IACpF,CAAC;AACH,CAAC;AAED,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAc,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAClE,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACjD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,uBAAuB,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACtD,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,QAAQ,CAAC,yBAAyB,GAAG,EAAE,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAI,GAAG,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC3D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/D,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC5C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACjG,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,QAAgB;IAChE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAEpE,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9B,MAAM,UAAU,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC/B,MAAM,UAAU,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,QAAQ,IAAI,GAAG,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAChG,IAAI,CAAC,+BAA+B,QAAQ,IAAI,GAAG,QAAQ,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,GAAW,EAAE,MAAiB;IAC5E,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,QAAQ,CAAC,2BAA2B,MAAM,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAClD,MAAM,IAAI,QAAQ,CAChB,uDAAuD;YACrD,SAAS,GAAG,mCAAmC,GAAG,CAAC,QAAQ,GAAG,CACjE,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAc,EAAE,GAAW,EAAE,MAAiB;IAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,QAAQ,CAAC,2BAA2B,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,MAAM,UAAU,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1E,MAAM,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;IAErD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,IAAI,IAAI,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,UAAU,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IACjE,MAAM,UAAU,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,gBAAgB,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;IACtE,MAAM,UAAU,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9F,MAAM,QAAQ,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAiB,EACjB,KAAK,GAAa,EAAE,EACpB,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAEnB,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,QAAQ,CAChB,iDAAiD,GAAG,4CAA4C,CACjG,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,QAAQ,CAChB,gCAAgC,GAAG,mCAAmC,GAAG,CAAC,QAAQ,GAAG,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GACR,KAAK,CAAC,MAAM,GAAG,CAAC;QACd,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC;QACpB,CAAC,CAAC,CAAC,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC,MAAM,CAAC;YACV,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,UAAU,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAiB,EACjB,KAAK,GAAa,EAAE,EACpB,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAEnB,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,QAAQ,CAChB,iDAAiD,GAAG,4CAA4C,CACjG,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACvE,CAAC"}
|