@devkitvault/recall 1.0.0 → 1.0.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 +11 -22
- package/dist/commands/alias.js +84 -0
- package/dist/commands/approve.js +108 -0
- package/dist/commands/audit.js +58 -0
- package/dist/commands/auth/forgot.js +33 -0
- package/dist/commands/auth/index.js +16 -0
- package/dist/commands/auth/login.js +151 -0
- package/dist/commands/auth/logout.js +15 -0
- package/dist/commands/auth/register.js +84 -0
- package/dist/commands/auth/reset.js +43 -0
- package/dist/commands/completion.js +158 -0
- package/dist/commands/config.js +89 -0
- package/dist/commands/delete.js +58 -0
- package/dist/commands/doctor.js +106 -0
- package/dist/commands/env.js +135 -0
- package/dist/commands/export.js +68 -0
- package/dist/commands/feedback.js +25 -0
- package/dist/commands/group.js +206 -0
- package/dist/commands/history.js +41 -0
- package/dist/commands/import.js +126 -0
- package/dist/commands/list.js +69 -0
- package/dist/commands/org.js +408 -0
- package/dist/commands/pin.js +30 -0
- package/dist/commands/run.js +55 -0
- package/dist/commands/save.js +73 -0
- package/dist/commands/search.js +82 -0
- package/dist/commands/share.js +88 -0
- package/dist/commands/snippet.js +147 -0
- package/dist/commands/support.js +96 -0
- package/dist/commands/sync.js +80 -0
- package/dist/commands/template.js +177 -0
- package/dist/commands/update.js +66 -0
- package/dist/commands/upgrade.js +122 -0
- package/dist/commands/whoami.js +70 -0
- package/dist/index.js +71 -0
- package/dist/lib/api.js +51 -0
- package/dist/lib/auth.js +88 -0
- package/dist/lib/config.js +37 -0
- package/package.json +12 -7
- package/bin/recall.js +0 -169
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.orgCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
10
|
+
const commander_1 = require("commander");
|
|
11
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
+
const ora_1 = __importDefault(require("ora"));
|
|
13
|
+
const api_1 = require("../lib/api");
|
|
14
|
+
const auth_1 = require("../lib/auth");
|
|
15
|
+
function planError(err) {
|
|
16
|
+
if (err instanceof api_1.ApiError && err.status === 403) {
|
|
17
|
+
console.log(chalk_1.default.yellow('\n Org vaults require a Team plan.'));
|
|
18
|
+
console.log(chalk_1.default.dim(` Upgrade at ${chalk_1.default.white('https://devkitvault.com/recall/upgrade')}`));
|
|
19
|
+
console.log(chalk_1.default.dim(' Or run: recall upgrade\n'));
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
async function getMyOrg(token) {
|
|
25
|
+
const { org } = await api_1.ApiClient.get('/orgs/me', token);
|
|
26
|
+
return org;
|
|
27
|
+
}
|
|
28
|
+
// org create
|
|
29
|
+
const orgCreate = new commander_1.Command('create')
|
|
30
|
+
.argument('<name>', 'Organization name')
|
|
31
|
+
.description('Create a new organization')
|
|
32
|
+
.action(async (name) => {
|
|
33
|
+
const token = await (0, auth_1.requireAuth)();
|
|
34
|
+
const spinner = (0, ora_1.default)(`Creating org "${name}"...`).start();
|
|
35
|
+
try {
|
|
36
|
+
const org = await api_1.ApiClient.post('/orgs', { name }, token);
|
|
37
|
+
spinner.succeed(chalk_1.default.green(`Org "${org.name}" created`));
|
|
38
|
+
console.log(chalk_1.default.dim(`\n You are the admin.\n`));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
spinner.stop();
|
|
42
|
+
if (!planError(err)) {
|
|
43
|
+
if (err instanceof api_1.ApiError && err.status === 409) {
|
|
44
|
+
console.error(chalk_1.default.red(`\n Org "${name}" already exists\n`));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.error(chalk_1.default.red('\n Failed to create org\n'));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// org members
|
|
53
|
+
const orgMembers = new commander_1.Command('members')
|
|
54
|
+
.description('List all members in your org')
|
|
55
|
+
.action(async () => {
|
|
56
|
+
const token = await (0, auth_1.requireAuth)();
|
|
57
|
+
const spinner = (0, ora_1.default)('Fetching members...').start();
|
|
58
|
+
try {
|
|
59
|
+
const org = await getMyOrg(token);
|
|
60
|
+
const { members } = await api_1.ApiClient.get(`/orgs/${org.id}/members`, token);
|
|
61
|
+
spinner.stop();
|
|
62
|
+
const table = new cli_table3_1.default({
|
|
63
|
+
head: [chalk_1.default.cyan('Username'), chalk_1.default.cyan('Email'), chalk_1.default.cyan('Role'), chalk_1.default.cyan('Joined')],
|
|
64
|
+
style: { head: [], border: ['grey'] },
|
|
65
|
+
colWidths: [20, 28, 10, 14],
|
|
66
|
+
});
|
|
67
|
+
for (const m of members) {
|
|
68
|
+
const roleColor = m.role === 'admin'
|
|
69
|
+
? chalk_1.default.yellow(m.role)
|
|
70
|
+
: m.role === 'member'
|
|
71
|
+
? chalk_1.default.white(m.role)
|
|
72
|
+
: chalk_1.default.dim(m.role);
|
|
73
|
+
table.push([
|
|
74
|
+
m.username,
|
|
75
|
+
m.email,
|
|
76
|
+
roleColor,
|
|
77
|
+
new Date(m.joinedAt).toLocaleDateString(),
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
console.log(table.toString());
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
spinner.stop();
|
|
84
|
+
planError(err);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// org invite
|
|
88
|
+
const orgInvite = new commander_1.Command('invite')
|
|
89
|
+
.argument('<email>', 'Email to invite')
|
|
90
|
+
.description('Invite a member to your org')
|
|
91
|
+
.action(async (email) => {
|
|
92
|
+
const token = await (0, auth_1.requireAuth)();
|
|
93
|
+
const spinner = (0, ora_1.default)(`Inviting ${email}...`).start();
|
|
94
|
+
try {
|
|
95
|
+
const org = await getMyOrg(token);
|
|
96
|
+
const result = await api_1.ApiClient.post(`/orgs/${org.id}/invite`, { email }, token);
|
|
97
|
+
spinner.succeed(chalk_1.default.green(`Invited ${email}`));
|
|
98
|
+
console.log();
|
|
99
|
+
console.log(` ${chalk_1.default.dim('They can join with:')}`);
|
|
100
|
+
console.log(` ${chalk_1.default.white(result.joinCommand)}`);
|
|
101
|
+
console.log();
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
spinner.stop();
|
|
105
|
+
if (!planError(err)) {
|
|
106
|
+
console.error(chalk_1.default.red('\n Failed to invite member\n'));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// org join
|
|
111
|
+
const orgJoin = new commander_1.Command('join')
|
|
112
|
+
.argument('<token>', 'Invite token')
|
|
113
|
+
.description('Join an org using an invite token')
|
|
114
|
+
.action(async (token) => {
|
|
115
|
+
const authToken = await (0, auth_1.requireAuth)();
|
|
116
|
+
const spinner = (0, ora_1.default)('Joining org...').start();
|
|
117
|
+
try {
|
|
118
|
+
const result = await api_1.ApiClient.post('/orgs/join', { token }, authToken);
|
|
119
|
+
spinner.succeed(chalk_1.default.green(result.message));
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
spinner.stop();
|
|
123
|
+
if (!planError(err)) {
|
|
124
|
+
if (err instanceof api_1.ApiError) {
|
|
125
|
+
console.error(chalk_1.default.red(`\n ${err.message}\n`));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// org role
|
|
131
|
+
const orgRole = new commander_1.Command('role')
|
|
132
|
+
.argument('<username>', 'Member username')
|
|
133
|
+
.argument('<role>', 'Role: admin | member | viewer')
|
|
134
|
+
.description('Set a member role')
|
|
135
|
+
.action(async (username, role) => {
|
|
136
|
+
const token = await (0, auth_1.requireAuth)();
|
|
137
|
+
const spinner = (0, ora_1.default)('Updating role...').start();
|
|
138
|
+
try {
|
|
139
|
+
const org = await getMyOrg(token);
|
|
140
|
+
const { members } = await api_1.ApiClient.get(`/orgs/${org.id}/members`, token);
|
|
141
|
+
const member = members.find((m) => m.username === username);
|
|
142
|
+
if (!member) {
|
|
143
|
+
spinner.fail(chalk_1.default.red(`Member "${username}" not found`));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
await api_1.ApiClient.patch(`/orgs/${org.id}/members/${member.userId}`, { role }, token);
|
|
147
|
+
spinner.succeed(chalk_1.default.green(`Set ${username} role to "${role}"`));
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
spinner.stop();
|
|
151
|
+
if (!planError(err)) {
|
|
152
|
+
console.error(chalk_1.default.red('\n Failed to update role\n'));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
// org remove
|
|
157
|
+
const orgRemove = new commander_1.Command('remove')
|
|
158
|
+
.argument('<username>', 'Member username to remove')
|
|
159
|
+
.description('Remove a member from your org')
|
|
160
|
+
.action(async (username) => {
|
|
161
|
+
const token = await (0, auth_1.requireAuth)();
|
|
162
|
+
const { confirm } = await inquirer_1.default.prompt([{
|
|
163
|
+
type: 'confirm',
|
|
164
|
+
name: 'confirm',
|
|
165
|
+
message: chalk_1.default.red(`Remove "${username}" from the org?`),
|
|
166
|
+
default: false,
|
|
167
|
+
}]);
|
|
168
|
+
if (!confirm) {
|
|
169
|
+
console.log(chalk_1.default.dim('\n Cancelled.\n'));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const spinner = (0, ora_1.default)('Removing member...').start();
|
|
173
|
+
try {
|
|
174
|
+
const org = await getMyOrg(token);
|
|
175
|
+
const { members } = await api_1.ApiClient.get(`/orgs/${org.id}/members`, token);
|
|
176
|
+
const member = members.find((m) => m.username === username);
|
|
177
|
+
if (!member) {
|
|
178
|
+
spinner.fail(chalk_1.default.red(`Member "${username}" not found`));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await api_1.ApiClient.delete(`/orgs/${org.id}/members/${member.userId}`, token);
|
|
182
|
+
spinner.succeed(chalk_1.default.green(`Removed "${username}" from org`));
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
spinner.stop();
|
|
186
|
+
if (!planError(err)) {
|
|
187
|
+
console.error(chalk_1.default.red('\n Failed to remove member\n'));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// org leave
|
|
192
|
+
const orgLeave = new commander_1.Command('leave')
|
|
193
|
+
.description('Leave your current org')
|
|
194
|
+
.action(async () => {
|
|
195
|
+
const token = await (0, auth_1.requireAuth)();
|
|
196
|
+
const { confirm } = await inquirer_1.default.prompt([{
|
|
197
|
+
type: 'confirm',
|
|
198
|
+
name: 'confirm',
|
|
199
|
+
message: chalk_1.default.red('Leave your org?'),
|
|
200
|
+
default: false,
|
|
201
|
+
}]);
|
|
202
|
+
if (!confirm) {
|
|
203
|
+
console.log(chalk_1.default.dim('\n Cancelled.\n'));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const spinner = (0, ora_1.default)('Leaving org...').start();
|
|
207
|
+
try {
|
|
208
|
+
const org = await getMyOrg(token);
|
|
209
|
+
await api_1.ApiClient.delete(`/orgs/${org.id}/leave`, token);
|
|
210
|
+
spinner.succeed(chalk_1.default.green('Left the org'));
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
spinner.stop();
|
|
214
|
+
if (err instanceof api_1.ApiError) {
|
|
215
|
+
console.error(chalk_1.default.red(`\n ${err.message}\n`));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
// org delete
|
|
220
|
+
const orgDelete = new commander_1.Command('delete')
|
|
221
|
+
.description('Delete your org (permanent)')
|
|
222
|
+
.action(async () => {
|
|
223
|
+
const token = await (0, auth_1.requireAuth)();
|
|
224
|
+
const { confirm1 } = await inquirer_1.default.prompt([{
|
|
225
|
+
type: 'confirm',
|
|
226
|
+
name: 'confirm1',
|
|
227
|
+
message: chalk_1.default.red('Delete your org? This cannot be undone.'),
|
|
228
|
+
default: false,
|
|
229
|
+
}]);
|
|
230
|
+
if (!confirm1) {
|
|
231
|
+
console.log(chalk_1.default.dim('\n Cancelled.\n'));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const { confirm2 } = await inquirer_1.default.prompt([{
|
|
235
|
+
type: 'input',
|
|
236
|
+
name: 'confirm2',
|
|
237
|
+
message: 'Type DELETE to confirm:',
|
|
238
|
+
}]);
|
|
239
|
+
if (confirm2 !== 'DELETE') {
|
|
240
|
+
console.log(chalk_1.default.dim('\n Cancelled.\n'));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const spinner = (0, ora_1.default)('Deleting org...').start();
|
|
244
|
+
try {
|
|
245
|
+
const org = await getMyOrg(token);
|
|
246
|
+
await api_1.ApiClient.delete(`/orgs/${org.id}`, token);
|
|
247
|
+
spinner.succeed(chalk_1.default.green('Org deleted'));
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
spinner.stop();
|
|
251
|
+
if (!planError(err)) {
|
|
252
|
+
console.error(chalk_1.default.red('\n Failed to delete org\n'));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
// org save
|
|
257
|
+
const orgSave = new commander_1.Command('save')
|
|
258
|
+
.argument('<command>', 'Command to save')
|
|
259
|
+
.option('-n, --name <name>', 'Command name')
|
|
260
|
+
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
261
|
+
.option('-a, --approval', 'Require approval before running')
|
|
262
|
+
.description('Save a command to the org vault')
|
|
263
|
+
.action(async (command, opts) => {
|
|
264
|
+
const token = await (0, auth_1.requireAuth)();
|
|
265
|
+
const spinner = (0, ora_1.default)('Saving to org vault...').start();
|
|
266
|
+
try {
|
|
267
|
+
const org = await getMyOrg(token);
|
|
268
|
+
const saved = await api_1.ApiClient.post(`/orgs/${org.id}/commands`, {
|
|
269
|
+
command,
|
|
270
|
+
name: opts.name,
|
|
271
|
+
tags: opts.tags?.split(',').map((t) => t.trim()),
|
|
272
|
+
requireApproval: opts.approval ?? false,
|
|
273
|
+
}, token);
|
|
274
|
+
spinner.succeed(chalk_1.default.green(`Saved${saved.name ? ` as "${saved.name}"` : ''} to org vault`) +
|
|
275
|
+
(opts.approval ? chalk_1.default.yellow(' — requires approval to run') : ''));
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
spinner.stop();
|
|
279
|
+
if (!planError(err)) {
|
|
280
|
+
console.error(chalk_1.default.red('\n Failed to save command\n'));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
// org commands
|
|
285
|
+
const orgCommands2 = new commander_1.Command('commands')
|
|
286
|
+
.description('List all commands in the org vault')
|
|
287
|
+
.action(async () => {
|
|
288
|
+
const token = await (0, auth_1.requireAuth)();
|
|
289
|
+
const spinner = (0, ora_1.default)('Fetching org commands...').start();
|
|
290
|
+
try {
|
|
291
|
+
const org = await getMyOrg(token);
|
|
292
|
+
const { commands } = await api_1.ApiClient.get(`/orgs/${org.id}/commands`, token);
|
|
293
|
+
spinner.stop();
|
|
294
|
+
if (!commands.length) {
|
|
295
|
+
console.log(chalk_1.default.dim('\n No commands in org vault yet.\n'));
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const table = new cli_table3_1.default({
|
|
299
|
+
head: [chalk_1.default.cyan('Name'), chalk_1.default.cyan('Command'), chalk_1.default.cyan('Tags')],
|
|
300
|
+
style: { head: [], border: ['grey'] },
|
|
301
|
+
colWidths: [20, 46, 18],
|
|
302
|
+
wordWrap: true,
|
|
303
|
+
});
|
|
304
|
+
for (const cmd of commands) {
|
|
305
|
+
table.push([
|
|
306
|
+
cmd.name ?? chalk_1.default.dim('—'),
|
|
307
|
+
cmd.command.length > 44
|
|
308
|
+
? cmd.command.slice(0, 44) + '…'
|
|
309
|
+
: cmd.command,
|
|
310
|
+
(cmd.tags ?? []).join(', ') || chalk_1.default.dim('—'),
|
|
311
|
+
]);
|
|
312
|
+
}
|
|
313
|
+
console.log(table.toString());
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
spinner.stop();
|
|
317
|
+
planError(err);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// org run
|
|
321
|
+
const orgRun = new commander_1.Command('run')
|
|
322
|
+
.argument('<name>', 'Command name')
|
|
323
|
+
.option('-d, --dry-run', 'Print without running')
|
|
324
|
+
.description('Run an org command by name')
|
|
325
|
+
.action(async (name, opts) => {
|
|
326
|
+
const token = await (0, auth_1.requireAuth)();
|
|
327
|
+
const spinner = (0, ora_1.default)('Looking up...').start();
|
|
328
|
+
try {
|
|
329
|
+
const org = await getMyOrg(token);
|
|
330
|
+
const { commands } = await api_1.ApiClient.get(`/orgs/${org.id}/commands`, token);
|
|
331
|
+
const cmd = commands.find((c) => c.name === name);
|
|
332
|
+
if (!cmd) {
|
|
333
|
+
spinner.fail(chalk_1.default.red(`Command "${name}" not found in org vault`));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
spinner.stop();
|
|
337
|
+
// Check if approval required
|
|
338
|
+
if (cmd.requireApproval) {
|
|
339
|
+
console.log(chalk_1.default.yellow(`\n This command requires approval before running.\n`));
|
|
340
|
+
console.log(` ${chalk_1.default.dim('Command:')} ${chalk_1.default.white(cmd.command)}`);
|
|
341
|
+
console.log();
|
|
342
|
+
console.log(chalk_1.default.dim(' Request approval with:'));
|
|
343
|
+
console.log(` ${chalk_1.default.cyan(`recall approve request ${name}`)}`);
|
|
344
|
+
console.log();
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
console.log(chalk_1.default.dim(`\n $ ${cmd.command}\n`));
|
|
348
|
+
if (!opts.dryRun) {
|
|
349
|
+
(0, child_process_1.execSync)(cmd.command, { stdio: 'inherit' });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
spinner.stop();
|
|
354
|
+
planError(err);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
// org search
|
|
358
|
+
const orgSearch = new commander_1.Command('search')
|
|
359
|
+
.argument('<query>', 'Search query')
|
|
360
|
+
.description('Search commands in the org vault')
|
|
361
|
+
.action(async (query) => {
|
|
362
|
+
const token = await (0, auth_1.requireAuth)();
|
|
363
|
+
const spinner = (0, ora_1.default)('Searching...').start();
|
|
364
|
+
try {
|
|
365
|
+
const org = await getMyOrg(token);
|
|
366
|
+
const { commands } = await api_1.ApiClient.get(`/orgs/${org.id}/commands/search?q=${encodeURIComponent(query)}`, token);
|
|
367
|
+
spinner.stop();
|
|
368
|
+
if (!commands.length) {
|
|
369
|
+
console.log(chalk_1.default.dim(`\n No results for "${query}"\n`));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
console.log(chalk_1.default.dim(`\n ${commands.length} result(s) for "${chalk_1.default.white(query)}"\n`));
|
|
373
|
+
const table = new cli_table3_1.default({
|
|
374
|
+
head: [chalk_1.default.cyan('Name'), chalk_1.default.cyan('Command'), chalk_1.default.cyan('Tags')],
|
|
375
|
+
style: { head: [], border: ['grey'] },
|
|
376
|
+
colWidths: [20, 46, 18],
|
|
377
|
+
wordWrap: true,
|
|
378
|
+
});
|
|
379
|
+
for (const cmd of commands) {
|
|
380
|
+
table.push([
|
|
381
|
+
cmd.name ?? chalk_1.default.dim('—'),
|
|
382
|
+
cmd.command.length > 44
|
|
383
|
+
? cmd.command.slice(0, 44) + '…'
|
|
384
|
+
: cmd.command,
|
|
385
|
+
(cmd.tags ?? []).join(', ') || chalk_1.default.dim('—'),
|
|
386
|
+
]);
|
|
387
|
+
}
|
|
388
|
+
console.log(table.toString());
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
spinner.stop();
|
|
392
|
+
planError(err);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
exports.orgCommand = new commander_1.Command('org')
|
|
396
|
+
.description('Manage org vaults (Team plan)')
|
|
397
|
+
.addCommand(orgCreate)
|
|
398
|
+
.addCommand(orgMembers)
|
|
399
|
+
.addCommand(orgInvite)
|
|
400
|
+
.addCommand(orgJoin)
|
|
401
|
+
.addCommand(orgRole)
|
|
402
|
+
.addCommand(orgRemove)
|
|
403
|
+
.addCommand(orgLeave)
|
|
404
|
+
.addCommand(orgDelete)
|
|
405
|
+
.addCommand(orgSave)
|
|
406
|
+
.addCommand(orgCommands2)
|
|
407
|
+
.addCommand(orgRun)
|
|
408
|
+
.addCommand(orgSearch);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.pinCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const api_1 = require("../lib/api");
|
|
11
|
+
const auth_1 = require("../lib/auth");
|
|
12
|
+
exports.pinCommand = new commander_1.Command('pin')
|
|
13
|
+
.description('Pin or unpin a command to the top of your list')
|
|
14
|
+
.argument('<name>', 'Command name')
|
|
15
|
+
.option('-u, --unpin', 'Unpin instead of pin')
|
|
16
|
+
.action(async (name, opts) => {
|
|
17
|
+
const token = await (0, auth_1.requireAuth)();
|
|
18
|
+
const spinner = (0, ora_1.default)('Looking up...').start();
|
|
19
|
+
try {
|
|
20
|
+
const cmd = await api_1.ApiClient.get(`/commands/by-name/${encodeURIComponent(name)}`, token);
|
|
21
|
+
const pinned = !opts.unpin;
|
|
22
|
+
await api_1.ApiClient.patch(`/commands/${cmd.id}/pin`, { pinned }, token);
|
|
23
|
+
spinner.succeed(pinned
|
|
24
|
+
? chalk_1.default.green(`Pinned "${name}" — it will appear at the top of recall list`)
|
|
25
|
+
: chalk_1.default.green(`Unpinned "${name}"`));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
spinner.fail(chalk_1.default.red(`Command "${name}" not found`));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const ora_1 = __importDefault(require("ora"));
|
|
12
|
+
const api_1 = require("../lib/api");
|
|
13
|
+
const auth_1 = require("../lib/auth");
|
|
14
|
+
exports.runCommand = new commander_1.Command('run')
|
|
15
|
+
.description('Run a saved command by name or alias')
|
|
16
|
+
.argument('<name>', 'Command name or alias')
|
|
17
|
+
.option('-d, --dry-run', 'Print command without running it')
|
|
18
|
+
.option('-c, --confirm', 'Ask for confirmation before running')
|
|
19
|
+
.action(async (name, opts) => {
|
|
20
|
+
const token = await (0, auth_1.requireAuth)();
|
|
21
|
+
const spinner = (0, ora_1.default)('Looking up...').start();
|
|
22
|
+
try {
|
|
23
|
+
// Try by name first, then alias
|
|
24
|
+
let cmd;
|
|
25
|
+
try {
|
|
26
|
+
cmd = await api_1.ApiClient.get(`/commands/by-name/${encodeURIComponent(name)}`, token);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
cmd = await api_1.ApiClient.get(`/aliases/${encodeURIComponent(name)}/resolve`, token);
|
|
30
|
+
}
|
|
31
|
+
spinner.stop();
|
|
32
|
+
console.log(chalk_1.default.dim(`\n $ ${cmd.command}\n`));
|
|
33
|
+
if (opts.dryRun)
|
|
34
|
+
return;
|
|
35
|
+
// Confirm if --confirm flag or command has confirm flag
|
|
36
|
+
if (opts.confirm) {
|
|
37
|
+
const { ok } = await inquirer_1.default.prompt([{
|
|
38
|
+
type: 'confirm',
|
|
39
|
+
name: 'ok',
|
|
40
|
+
message: 'Run this command?',
|
|
41
|
+
default: true,
|
|
42
|
+
}]);
|
|
43
|
+
if (!ok) {
|
|
44
|
+
console.log(chalk_1.default.dim(' Cancelled.\n'));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Log the run
|
|
49
|
+
api_1.ApiClient.post(`/commands/${cmd.id}/run`, {}, token).catch(() => { });
|
|
50
|
+
(0, child_process_1.execSync)(cmd.command, { stdio: 'inherit' });
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
spinner.fail(chalk_1.default.red(`Command "${name}" not found`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.saveCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const api_1 = require("../lib/api");
|
|
11
|
+
const auth_1 = require("../lib/auth");
|
|
12
|
+
exports.saveCommand = new commander_1.Command('save')
|
|
13
|
+
.description('Save a command to your vault')
|
|
14
|
+
.argument('<command>', 'The shell command to save')
|
|
15
|
+
.option('-n, --name <name>', 'Short memorable name')
|
|
16
|
+
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
17
|
+
.option('-g, --group <group>', 'Save directly to a group (Pro)')
|
|
18
|
+
.action(async (command, opts) => {
|
|
19
|
+
const token = await (0, auth_1.requireAuth)();
|
|
20
|
+
let groupId;
|
|
21
|
+
// Resolve group name to ID if provided
|
|
22
|
+
if (opts.group) {
|
|
23
|
+
const spinner = (0, ora_1.default)(`Looking up group "${opts.group}"...`).start();
|
|
24
|
+
try {
|
|
25
|
+
const group = await api_1.ApiClient.get(`/groups/by-name/${encodeURIComponent(opts.group)}`, token);
|
|
26
|
+
groupId = group.id;
|
|
27
|
+
spinner.stop();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err instanceof api_1.ApiError && err.status === 403) {
|
|
31
|
+
spinner.stop();
|
|
32
|
+
console.log(chalk_1.default.yellow('\n Groups require a Pro plan.'));
|
|
33
|
+
console.log(chalk_1.default.dim(' Run: recall upgrade\n'));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
if (err instanceof api_1.ApiError && err.status === 404) {
|
|
37
|
+
// Auto-create the group
|
|
38
|
+
spinner.text = `Creating group "${opts.group}"...`;
|
|
39
|
+
try {
|
|
40
|
+
const newGroup = await api_1.ApiClient.post('/groups', { name: opts.group }, token);
|
|
41
|
+
groupId = newGroup.id;
|
|
42
|
+
spinner.succeed(chalk_1.default.green(`Created group "${opts.group}"`));
|
|
43
|
+
}
|
|
44
|
+
catch (createErr) {
|
|
45
|
+
spinner.fail(chalk_1.default.red(`Failed to create group "${opts.group}"`));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
spinner.fail(chalk_1.default.red(`Failed to look up group "${opts.group}"`));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const spinner = (0, ora_1.default)('Saving...').start();
|
|
56
|
+
try {
|
|
57
|
+
const saved = await api_1.ApiClient.post('/commands', {
|
|
58
|
+
command,
|
|
59
|
+
name: opts.name,
|
|
60
|
+
tags: opts.tags?.split(',').map((t) => t.trim()),
|
|
61
|
+
groupId,
|
|
62
|
+
}, token);
|
|
63
|
+
spinner.succeed(chalk_1.default.green(`Saved${saved.name ? ` as "${saved.name}"` : ''}` +
|
|
64
|
+
(opts.group ? chalk_1.default.dim(` → group "${opts.group}"`) : '')));
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
spinner.fail(chalk_1.default.red('Failed to save'));
|
|
68
|
+
if (err instanceof api_1.ApiError && err.status === 403 && err.body?.upgrade) {
|
|
69
|
+
console.log(chalk_1.default.yellow('\n This feature requires an upgrade.'));
|
|
70
|
+
console.log(chalk_1.default.dim(` Upgrade at ${chalk_1.default.white(err.body.upgrade)}\n`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.searchCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const api_1 = require("../lib/api");
|
|
12
|
+
const auth_1 = require("../lib/auth");
|
|
13
|
+
function highlight(text, query) {
|
|
14
|
+
if (!query)
|
|
15
|
+
return text;
|
|
16
|
+
const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
17
|
+
return text.replace(regex, chalk_1.default.yellow.bold('$1'));
|
|
18
|
+
}
|
|
19
|
+
function fuzzyMatch(text, query) {
|
|
20
|
+
const t = text.toLowerCase();
|
|
21
|
+
const q = query.toLowerCase();
|
|
22
|
+
// exact substring match
|
|
23
|
+
if (t.includes(q))
|
|
24
|
+
return true;
|
|
25
|
+
// fuzzy — all chars of query appear in order in text
|
|
26
|
+
let i = 0;
|
|
27
|
+
for (const ch of t) {
|
|
28
|
+
if (ch === q[i])
|
|
29
|
+
i++;
|
|
30
|
+
if (i === q.length)
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
exports.searchCommand = new commander_1.Command('search')
|
|
36
|
+
.description('Search your saved commands')
|
|
37
|
+
.argument('<query>', 'Search query')
|
|
38
|
+
.option('-t, --tag <tag>', 'Filter by tag')
|
|
39
|
+
.action(async (query, opts) => {
|
|
40
|
+
const token = await (0, auth_1.requireAuth)();
|
|
41
|
+
const spinner = (0, ora_1.default)('Searching...').start();
|
|
42
|
+
try {
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
if (opts.tag)
|
|
45
|
+
params.set('tag', opts.tag);
|
|
46
|
+
const { commands } = await api_1.ApiClient.get(`/commands?${params}`, token);
|
|
47
|
+
spinner.stop();
|
|
48
|
+
// Fuzzy filter locally
|
|
49
|
+
const results = commands.filter((cmd) => {
|
|
50
|
+
return (fuzzyMatch(cmd.command, query) ||
|
|
51
|
+
(cmd.name && fuzzyMatch(cmd.name, query)) ||
|
|
52
|
+
(cmd.tags && cmd.tags.some((t) => fuzzyMatch(t, query))));
|
|
53
|
+
});
|
|
54
|
+
if (!results.length) {
|
|
55
|
+
console.log(chalk_1.default.dim(`\n No commands found matching "${query}".\n`));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
console.log(chalk_1.default.dim(`\n ${results.length} result${results.length === 1 ? '' : 's'} for "${chalk_1.default.white(query)}"\n`));
|
|
59
|
+
const table = new cli_table3_1.default({
|
|
60
|
+
head: [chalk_1.default.cyan('Name'), chalk_1.default.cyan('Command'), chalk_1.default.cyan('Tags')],
|
|
61
|
+
style: { head: [], border: ['grey'] },
|
|
62
|
+
colWidths: [20, 46, 18],
|
|
63
|
+
wordWrap: true,
|
|
64
|
+
});
|
|
65
|
+
for (const cmd of results) {
|
|
66
|
+
const name = cmd.name ?? '—';
|
|
67
|
+
const command = cmd.command.length > 44
|
|
68
|
+
? cmd.command.slice(0, 44) + '…'
|
|
69
|
+
: cmd.command;
|
|
70
|
+
const tags = (cmd.tags ?? []).join(', ') || '—';
|
|
71
|
+
table.push([
|
|
72
|
+
highlight(name, query),
|
|
73
|
+
highlight(command, query),
|
|
74
|
+
highlight(tags, query),
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
console.log(table.toString());
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
spinner.fail(chalk_1.default.red('Search failed'));
|
|
81
|
+
}
|
|
82
|
+
});
|