microgit-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +32 -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 +91 -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 +100 -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,91 @@
|
|
|
1
|
+
import { clearStoredConfig, configFilePath, loadConfig, normalizeUrl, readStoredConfig, writeStoredConfig, } from '../config/index.js';
|
|
2
|
+
import { BIN } from '../config/settings.js';
|
|
3
|
+
import { fail, formatDate, info, ok, printJson } from '../ui/index.js';
|
|
4
|
+
function mask(value) {
|
|
5
|
+
if (value.length <= 8)
|
|
6
|
+
return 'saved';
|
|
7
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
8
|
+
}
|
|
9
|
+
export async function configCommand(argv) {
|
|
10
|
+
const json = argv.includes('--json');
|
|
11
|
+
const args = argv.filter((arg) => arg !== '--json');
|
|
12
|
+
const [action, key, ...rest] = args;
|
|
13
|
+
if (!action || action === 'show' || action === '--help' || action === '-h') {
|
|
14
|
+
showConfig(json);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (action === 'path') {
|
|
18
|
+
console.log(configFilePath());
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (action === 'clear') {
|
|
22
|
+
clearStoredConfig();
|
|
23
|
+
ok(`Removed ${configFilePath()}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (action === 'set') {
|
|
27
|
+
const name = key?.trim();
|
|
28
|
+
const value = rest.join(' ').trim();
|
|
29
|
+
if (!name || value === '') {
|
|
30
|
+
fail(`Usage: ${BIN} config set <url|username|token|password> <value>`);
|
|
31
|
+
}
|
|
32
|
+
const stored = readStoredConfig();
|
|
33
|
+
const next = { ...stored };
|
|
34
|
+
if (name === 'url')
|
|
35
|
+
next.url = normalizeUrl(value);
|
|
36
|
+
else if (name === 'username')
|
|
37
|
+
next.username = value;
|
|
38
|
+
else if (name === 'token')
|
|
39
|
+
next.token = value;
|
|
40
|
+
else if (name === 'password')
|
|
41
|
+
next.password = value;
|
|
42
|
+
else
|
|
43
|
+
fail(`Unknown config key '${name}'. Use url, username, token or password.`);
|
|
44
|
+
writeStoredConfig(next);
|
|
45
|
+
ok(`Set ${name}.`);
|
|
46
|
+
if (name === 'password')
|
|
47
|
+
info('The password is stored in plain text in the config file.');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (action === 'unset') {
|
|
51
|
+
const name = key?.trim();
|
|
52
|
+
if (!name)
|
|
53
|
+
fail(`Usage: ${BIN} config unset <token|password|username|expiresAt>`);
|
|
54
|
+
const stored = readStoredConfig();
|
|
55
|
+
const next = { ...stored };
|
|
56
|
+
if (name === 'token' || name === 'password' || name === 'username' || name === 'expiresAt') {
|
|
57
|
+
delete next[name];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
fail(`Unknown config key '${name}'.`);
|
|
61
|
+
}
|
|
62
|
+
writeStoredConfig(next);
|
|
63
|
+
ok(`Unset ${name}.`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
fail(`Unknown config action '${action}'. Try: ${BIN} config`);
|
|
67
|
+
}
|
|
68
|
+
function showConfig(json) {
|
|
69
|
+
const config = loadConfig();
|
|
70
|
+
const stored = readStoredConfig();
|
|
71
|
+
const token = config.token;
|
|
72
|
+
if (json) {
|
|
73
|
+
printJson({
|
|
74
|
+
configPath: config.configPath,
|
|
75
|
+
url: config.url,
|
|
76
|
+
username: config.username ?? null,
|
|
77
|
+
token: token ? 'saved' : null,
|
|
78
|
+
expiresAt: config.expiresAt ?? null,
|
|
79
|
+
password: config.password ? 'saved' : null,
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const row = (label, value) => ` ${label.padEnd(12)} ${value}`;
|
|
84
|
+
console.log(row('Config file', config.configPath));
|
|
85
|
+
console.log(row('Server', config.url));
|
|
86
|
+
console.log(row('Username', config.username ?? '-'));
|
|
87
|
+
console.log(row('Token', token ? mask(token) : '-'));
|
|
88
|
+
console.log(row('Expires', formatDate(config.expiresAt)));
|
|
89
|
+
console.log(row('Password', stored.password ? 'saved (plain text)' : '-'));
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import { loadConfig } from '../config/index.js';
|
|
5
|
+
import { ensureIdentity, git, gitChecked, pullRepo, pushRepo, requireGitRepo } from '../git/index.js';
|
|
6
|
+
import { BIN } from '../config/settings.js';
|
|
7
|
+
import { ask, fail, info, ok } from '../ui/index.js';
|
|
8
|
+
import { text, trimmed } from '../util/index.js';
|
|
9
|
+
export async function initCommand(argv) {
|
|
10
|
+
const { values, positionals } = parseArgs({
|
|
11
|
+
args: argv,
|
|
12
|
+
allowPositionals: true,
|
|
13
|
+
options: {
|
|
14
|
+
dir: { type: 'string' },
|
|
15
|
+
help: { type: 'boolean', short: 'h' },
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
if (values.help) {
|
|
19
|
+
console.log(`Usage: ${BIN} init [directory] [options]
|
|
20
|
+
|
|
21
|
+
Create a project folder, run 'git init' in it and add an empty README.md.
|
|
22
|
+
Nothing is committed or pushed, and no server repository is created.
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--dir <path> Directory to use (default: the given name, else this folder)
|
|
26
|
+
-h, --help Show this help
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
${BIN} init app
|
|
30
|
+
${BIN} init # initialise the current folder
|
|
31
|
+
${BIN} init --dir ./projects/app`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const name = trimmed(positionals[0]);
|
|
35
|
+
const target = path.resolve(trimmed(values.dir) ?? name ?? '.');
|
|
36
|
+
const label = path.basename(target) === '' ? target : path.basename(target);
|
|
37
|
+
if (fs.existsSync(path.join(target, '.git'))) {
|
|
38
|
+
fail(`Already a git repository: ${target}`);
|
|
39
|
+
}
|
|
40
|
+
fs.mkdirSync(target, { recursive: true });
|
|
41
|
+
await gitChecked(['init', '-b', 'main'], { cwd: target, inherit: false });
|
|
42
|
+
let username;
|
|
43
|
+
try {
|
|
44
|
+
username = loadConfig().username;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
username = undefined;
|
|
48
|
+
}
|
|
49
|
+
if (username)
|
|
50
|
+
await ensureIdentity(target, username);
|
|
51
|
+
const readme = path.join(target, 'README.md');
|
|
52
|
+
if (!fs.existsSync(readme))
|
|
53
|
+
fs.writeFileSync(readme, '', 'utf8');
|
|
54
|
+
ok(`Initialised ${label} with git and an empty README.md`);
|
|
55
|
+
info(`Next: cd ${label} · ${BIN} status`);
|
|
56
|
+
}
|
|
57
|
+
export async function statusCommand(argv) {
|
|
58
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
59
|
+
console.log(`Usage: ${BIN} status`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
await requireGitRepo();
|
|
63
|
+
await gitChecked(['status', '--short', '--branch']);
|
|
64
|
+
}
|
|
65
|
+
export async function commitCommand(argv) {
|
|
66
|
+
const { values } = parseArgs({
|
|
67
|
+
args: argv,
|
|
68
|
+
allowPositionals: true,
|
|
69
|
+
options: {
|
|
70
|
+
message: { type: 'string', short: 'm' },
|
|
71
|
+
'no-add': { type: 'boolean' },
|
|
72
|
+
'no-push': { type: 'boolean' },
|
|
73
|
+
'allow-empty': { type: 'boolean' },
|
|
74
|
+
url: { type: 'string' },
|
|
75
|
+
help: { type: 'boolean', short: 'h' },
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
if (values.help) {
|
|
79
|
+
console.log(`Usage: ${BIN} commit -m "message" [options]
|
|
80
|
+
|
|
81
|
+
Stage every change, commit it and push the current branch.
|
|
82
|
+
|
|
83
|
+
Options:
|
|
84
|
+
-m, --message <text> Commit message (asked interactively when omitted)
|
|
85
|
+
--no-add Do not run 'git add -A' first
|
|
86
|
+
--no-push Commit only, do not push
|
|
87
|
+
--allow-empty Allow a commit without staged changes
|
|
88
|
+
--url <url> Server URL (for credentials)
|
|
89
|
+
-h, --help Show this help
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
${BIN} commit -m "Add login page"
|
|
93
|
+
${BIN} commit -m "WIP" --no-push`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
await requireGitRepo();
|
|
97
|
+
const cwd = process.cwd();
|
|
98
|
+
let message = text(values.message)?.trim();
|
|
99
|
+
if (!message) {
|
|
100
|
+
if (!process.stdin.isTTY)
|
|
101
|
+
fail('Commit message is required. Use -m "message".');
|
|
102
|
+
message = await ask('Commit message: ');
|
|
103
|
+
}
|
|
104
|
+
if (!message)
|
|
105
|
+
fail('Commit message cannot be empty.');
|
|
106
|
+
const config = loadConfig(text(values.url));
|
|
107
|
+
if (!values['no-add']) {
|
|
108
|
+
await gitChecked(['add', '-A'], { cwd, inherit: false });
|
|
109
|
+
}
|
|
110
|
+
const diff = await git(['diff', '--cached', '--quiet'], { cwd });
|
|
111
|
+
if (diff.code > 1) {
|
|
112
|
+
fail(diff.stderr.trim() || 'Could not inspect the staged changes.');
|
|
113
|
+
}
|
|
114
|
+
let committed = false;
|
|
115
|
+
if (diff.code === 0 && !values['allow-empty']) {
|
|
116
|
+
info('Nothing to commit.');
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const commitArgs = ['commit', '-m', message];
|
|
120
|
+
if (values['allow-empty'])
|
|
121
|
+
commitArgs.push('--allow-empty');
|
|
122
|
+
await gitChecked(commitArgs, { cwd });
|
|
123
|
+
committed = true;
|
|
124
|
+
}
|
|
125
|
+
if (values['no-push']) {
|
|
126
|
+
if (committed)
|
|
127
|
+
ok('Committed.');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
await pushRepo(config, [], cwd);
|
|
131
|
+
if (committed)
|
|
132
|
+
ok('Committed and pushed.');
|
|
133
|
+
else
|
|
134
|
+
ok('Pushed.');
|
|
135
|
+
}
|
|
136
|
+
export async function pushCommand(argv) {
|
|
137
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
138
|
+
console.log(`Usage: ${BIN} push [git push arguments]
|
|
139
|
+
|
|
140
|
+
Push the current branch with the saved credentials. Extra arguments are passed
|
|
141
|
+
to git, for example: ${BIN} push --force-with-lease`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const config = loadConfig();
|
|
145
|
+
await pushRepo(config, argv);
|
|
146
|
+
ok('Pushed.');
|
|
147
|
+
}
|
|
148
|
+
export async function pullCommand(argv) {
|
|
149
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
150
|
+
console.log(`Usage: ${BIN} pull [git pull arguments]
|
|
151
|
+
|
|
152
|
+
Pull from origin with the saved credentials. Extra arguments are passed to git.`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const config = loadConfig();
|
|
156
|
+
await pullRepo(config, argv);
|
|
157
|
+
ok('Pulled.');
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/commands/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtG,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAc;IAC9C,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI,EAAE,IAAI;QACV,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE;YACP,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACvB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACtC;KACF,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG;;;;;;;;;;IAUzB,GAAG;IACH,GAAG;IACH,GAAG,4BAA4B,CAAC,CAAC;QACjC,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE5E,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;IAC9C,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;IAE1E,IAAI,QAA4B,CAAC;IACjC,IAAI,CAAC;QACH,QAAQ,GAAG,UAAU,EAAE,CAAC,QAAQ,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,GAAG,SAAS,CAAC;IACvB,CAAC;IACD,IAAI,QAAQ;QAAE,MAAM,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAErD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IAEjE,EAAE,CAAC,eAAe,KAAK,kCAAkC,CAAC,CAAC;IAC3D,IAAI,CAAC,YAAY,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc;IAChD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;QACpC,OAAO;IACT,CAAC;IACD,MAAM,cAAc,EAAE,CAAC;IACvB,MAAM,UAAU,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc;IAChD,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,IAAI,EAAE,IAAI;QACV,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE;YACP,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YACvC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC7B,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC9B,aAAa,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAClC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACvB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACtC;KACF,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG;;;;;;;;;;;;;IAazB,GAAG;IACH,GAAG,4BAA4B,CAAC,CAAC;QACjC,OAAO;IACT,CAAC;IAED,MAAM,cAAc,EAAE,CAAC;IACvB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE1B,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;YAAE,IAAI,CAAC,+CAA+C,CAAC,CAAC;QAChF,OAAO,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,CAAC,OAAO;QAAE,IAAI,CAAC,iCAAiC,CAAC,CAAC;IAEtD,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtB,MAAM,UAAU,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACjE,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,uCAAuC,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9C,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7C,IAAI,MAAM,CAAC,aAAa,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC5D,MAAM,UAAU,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACtC,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACtB,IAAI,SAAS;YAAE,EAAE,CAAC,YAAY,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAChC,IAAI,SAAS;QAAE,EAAE,CAAC,uBAAuB,CAAC,CAAC;;QACtC,EAAE,CAAC,SAAS,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAc;IAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG;;;uBAGN,GAAG,0BAA0B,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7B,EAAE,CAAC,SAAS,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAc;IAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG;;gFAEmD,CAAC,CAAC;QAC9E,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7B,EAAE,CAAC,SAAS,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { loginCommand, logoutCommand, registerCommand, whoamiCommand } from './auth.js';
|
|
2
|
+
import { configCommand } from './config.js';
|
|
3
|
+
import { commitCommand, initCommand, pullCommand, pushCommand, statusCommand } from './git.js';
|
|
4
|
+
import { cloneCommand, deleteCommand, listCommand, newCommand } from './repos.js';
|
|
5
|
+
import { CliError } from '../error/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* One table drives the entry point, `help` and the shell's tab completion, so a
|
|
8
|
+
* new command only has to be added here.
|
|
9
|
+
*/
|
|
10
|
+
export const COMMANDS = [
|
|
11
|
+
{
|
|
12
|
+
name: 'login',
|
|
13
|
+
group: 'Account',
|
|
14
|
+
summary: 'Sign in and store the API session',
|
|
15
|
+
flags: ['--url', '--username', '--password', '--save-password', '--forget-password', '--json', '--help'],
|
|
16
|
+
run: loginCommand,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'register',
|
|
20
|
+
group: 'Account',
|
|
21
|
+
summary: 'Create an account and sign in',
|
|
22
|
+
flags: ['--url', '--username', '--password', '--save-password', '--forget-password', '--json', '--help'],
|
|
23
|
+
run: registerCommand,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'logout',
|
|
27
|
+
group: 'Account',
|
|
28
|
+
summary: 'Revoke the session and clear local credentials',
|
|
29
|
+
flags: ['--url', '--json', '--help'],
|
|
30
|
+
run: logoutCommand,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'whoami',
|
|
34
|
+
aliases: ['me'],
|
|
35
|
+
group: 'Account',
|
|
36
|
+
summary: 'Show the signed-in account',
|
|
37
|
+
flags: ['--url', '--json', '--help'],
|
|
38
|
+
run: whoamiCommand,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'new',
|
|
42
|
+
aliases: ['create'],
|
|
43
|
+
group: 'Repositories',
|
|
44
|
+
summary: 'Create a repository (--private, --clone, --init)',
|
|
45
|
+
flags: ['--url', '--private', '--public', '--clone', '--init', '--dir', '--json', '--help'],
|
|
46
|
+
run: newCommand,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'list',
|
|
50
|
+
aliases: ['ls'],
|
|
51
|
+
group: 'Repositories',
|
|
52
|
+
summary: 'List repositories (--owner, --public, --private)',
|
|
53
|
+
flags: ['--url', '--owner', '--name', '--private', '--public', '--page', '--limit', '--json', '--help'],
|
|
54
|
+
run: listCommand,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'delete',
|
|
58
|
+
aliases: ['rm'],
|
|
59
|
+
group: 'Repositories',
|
|
60
|
+
summary: 'Delete a repository (owner/name for another account)',
|
|
61
|
+
flags: ['--url', '--yes', '--json', '--help'],
|
|
62
|
+
run: deleteCommand,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'clone',
|
|
66
|
+
group: 'Repositories',
|
|
67
|
+
summary: 'Clone a repository and use the server’s clone URL',
|
|
68
|
+
flags: ['--url', '--dir', '--json', '--help'],
|
|
69
|
+
run: cloneCommand,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'init',
|
|
73
|
+
group: 'Git',
|
|
74
|
+
summary: 'Start a local repository: folder, .git and an empty README',
|
|
75
|
+
flags: ['--dir', '--help'],
|
|
76
|
+
run: initCommand,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'status',
|
|
80
|
+
group: 'Git',
|
|
81
|
+
summary: 'Short git status of the current repository',
|
|
82
|
+
flags: ['--help'],
|
|
83
|
+
run: statusCommand,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'commit',
|
|
87
|
+
group: 'Git',
|
|
88
|
+
summary: 'Stage everything, commit and push',
|
|
89
|
+
flags: ['--url', '--message', '--no-add', '--no-push', '--allow-empty', '--help'],
|
|
90
|
+
run: commitCommand,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'push',
|
|
94
|
+
group: 'Git',
|
|
95
|
+
summary: 'Push with the saved credentials',
|
|
96
|
+
flags: ['--help'],
|
|
97
|
+
run: pushCommand,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'pull',
|
|
101
|
+
group: 'Git',
|
|
102
|
+
summary: 'Pull with the saved credentials',
|
|
103
|
+
flags: ['--help'],
|
|
104
|
+
run: pullCommand,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'config',
|
|
108
|
+
group: 'Config',
|
|
109
|
+
summary: 'Show or edit the config file and session',
|
|
110
|
+
flags: ['--json', '--help'],
|
|
111
|
+
run: configCommand,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
export const GROUPS = ['Account', 'Repositories', 'Git', 'Config'];
|
|
115
|
+
/** Commands the interactive shell handles itself. */
|
|
116
|
+
export const SHELL_BUILTINS = ['clear', 'exit', 'help'];
|
|
117
|
+
/** Look a command up by name or alias, case-insensitively. */
|
|
118
|
+
export function findCommand(name) {
|
|
119
|
+
const needle = name.trim().toLowerCase();
|
|
120
|
+
if (needle === '')
|
|
121
|
+
return undefined;
|
|
122
|
+
return COMMANDS.find((command) => command.name === needle || command.aliases?.includes(needle) === true);
|
|
123
|
+
}
|
|
124
|
+
/** Every accepted command word, sorted, for completion. */
|
|
125
|
+
export function commandNames() {
|
|
126
|
+
const names = COMMANDS.flatMap((command) => [command.name, ...(command.aliases ?? [])]);
|
|
127
|
+
return [...new Set(names)].sort();
|
|
128
|
+
}
|
|
129
|
+
/** Run one command line; unknown names fail like the one-shot CLI does. */
|
|
130
|
+
export async function dispatch(argv) {
|
|
131
|
+
const [name, ...rest] = argv;
|
|
132
|
+
const command = name === undefined ? undefined : findCommand(name);
|
|
133
|
+
if (command === undefined) {
|
|
134
|
+
const label = name === undefined ? '' : ` '${name}'`;
|
|
135
|
+
throw new CliError(`Unknown command${label}. Type \`help\` to list the commands.`);
|
|
136
|
+
}
|
|
137
|
+
await command.run(rest);
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/commands/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC/F,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAe7C;;;GAGG;AACH,MAAM,CAAC,MAAM,QAAQ,GAA2B;IAC9C;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,mCAAmC;QAC5C,KAAK,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACxG,GAAG,EAAE,YAAY;KAClB;IACD;QACE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,+BAA+B;QACxC,KAAK,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACxG,GAAG,EAAE,eAAe;KACrB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,gDAAgD;QACzD,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACpC,GAAG,EAAE,aAAa;KACnB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,CAAC,IAAI,CAAC;QACf,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,4BAA4B;QACrC,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACpC,GAAG,EAAE,aAAa;KACnB;IACD;QACE,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,CAAC,QAAQ,CAAC;QACnB,KAAK,EAAE,cAAc;QACrB,OAAO,EAAE,kDAAkD;QAC3D,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;QAC3F,GAAG,EAAE,UAAU;KAChB;IACD;QACE,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,CAAC,IAAI,CAAC;QACf,KAAK,EAAE,cAAc;QACrB,OAAO,EAAE,kDAAkD;QAC3D,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC;QACvG,GAAG,EAAE,WAAW;KACjB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,CAAC,IAAI,CAAC;QACf,KAAK,EAAE,cAAc;QACrB,OAAO,EAAE,sDAAsD;QAC/D,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;QAC7C,GAAG,EAAE,aAAa;KACnB;IACD;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,cAAc;QACrB,OAAO,EAAE,mDAAmD;QAC5D,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;QAC7C,GAAG,EAAE,YAAY;KAClB;IACD;QACE,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,4DAA4D;QACrE,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;QAC1B,GAAG,EAAE,WAAW;KACjB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,4CAA4C;QACrD,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,GAAG,EAAE,aAAa;KACnB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,mCAAmC;QAC5C,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,CAAC;QACjF,GAAG,EAAE,aAAa;KACnB;IACD;QACE,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,iCAAiC;QAC1C,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,GAAG,EAAE,WAAW;KACjB;IACD;QACE,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,iCAAiC;QAC1C,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,GAAG,EAAE,WAAW;KACjB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,QAAQ;QACf,OAAO,EAAE,0CAA0C;QACnD,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;QAC3B,GAAG,EAAE,aAAa;KACnB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAA4B,CAAC,SAAS,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAE5F,qDAAqD;AACrD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAEjE,8DAA8D;AAC9D,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACzC,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACpC,OAAO,QAAQ,CAAC,IAAI,CAClB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI,CACnF,CAAC;AACJ,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxF,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAc;IAC3C,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC;QACrD,MAAM,IAAI,QAAQ,CAAC,kBAAkB,KAAK,uCAAuC,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -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
|