@devkitvault/recall 1.0.0 → 1.0.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 +14 -21
- 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,158 @@
|
|
|
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.completionCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const ALL_COMMANDS = [
|
|
10
|
+
'auth', 'save', 'list', 'run', 'delete', 'search', 'update',
|
|
11
|
+
'export', 'import', 'sync', 'whoami', 'upgrade', 'config',
|
|
12
|
+
'pin', 'alias', 'history', 'share', 'snippet', 'env',
|
|
13
|
+
'group', 'org', 'audit', 'approve', 'template', 'doctor',
|
|
14
|
+
'completion',
|
|
15
|
+
];
|
|
16
|
+
const AUTH_CMDS = ['register', 'login', 'logout', 'forgot', 'reset'];
|
|
17
|
+
const GROUP_CMDS = ['create', 'list', 'show', 'add', 'remove', 'delete', 'rename'];
|
|
18
|
+
const ORG_CMDS = ['create', 'members', 'invite', 'join', 'role', 'remove', 'leave', 'delete', 'save', 'commands', 'run', 'search'];
|
|
19
|
+
const ALIAS_CMDS = ['set', 'list', 'delete'];
|
|
20
|
+
const SHARE_CMDS = ['create', 'list', 'delete'];
|
|
21
|
+
const SNIPPET_CMDS = ['save', 'list', 'show', 'run', 'delete'];
|
|
22
|
+
const ENV_CMDS = ['save', 'list', 'show', 'use', 'delete'];
|
|
23
|
+
const TEMPLATE_CMDS = ['save', 'list', 'show', 'run', 'delete'];
|
|
24
|
+
const APPROVE_CMDS = ['request', 'grant', 'list'];
|
|
25
|
+
const CONFIG_CMDS = ['show', 'set', 'switch', 'reset'];
|
|
26
|
+
function bashCompletion() {
|
|
27
|
+
return `# recall bash completion
|
|
28
|
+
# Add this to ~/.bashrc or ~/.bash_profile:
|
|
29
|
+
# eval "$(recall completion bash)"
|
|
30
|
+
|
|
31
|
+
_recall_completion() {
|
|
32
|
+
local cur prev words
|
|
33
|
+
COMPREPLY=()
|
|
34
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
35
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
36
|
+
words=("\${COMP_WORDS[@]}")
|
|
37
|
+
|
|
38
|
+
local commands="${ALL_COMMANDS.join(' ')}"
|
|
39
|
+
|
|
40
|
+
case "\${words[1]}" in
|
|
41
|
+
auth) COMPREPLY=($(compgen -W "${AUTH_CMDS.join(' ')}" -- "$cur")) ;;
|
|
42
|
+
group) COMPREPLY=($(compgen -W "${GROUP_CMDS.join(' ')}" -- "$cur")) ;;
|
|
43
|
+
org) COMPREPLY=($(compgen -W "${ORG_CMDS.join(' ')}" -- "$cur")) ;;
|
|
44
|
+
alias) COMPREPLY=($(compgen -W "${ALIAS_CMDS.join(' ')}" -- "$cur")) ;;
|
|
45
|
+
share) COMPREPLY=($(compgen -W "${SHARE_CMDS.join(' ')}" -- "$cur")) ;;
|
|
46
|
+
snippet) COMPREPLY=($(compgen -W "${SNIPPET_CMDS.join(' ')}" -- "$cur")) ;;
|
|
47
|
+
env) COMPREPLY=($(compgen -W "${ENV_CMDS.join(' ')}" -- "$cur")) ;;
|
|
48
|
+
template) COMPREPLY=($(compgen -W "${TEMPLATE_CMDS.join(' ')}" -- "$cur")) ;;
|
|
49
|
+
approve) COMPREPLY=($(compgen -W "${APPROVE_CMDS.join(' ')}" -- "$cur")) ;;
|
|
50
|
+
config) COMPREPLY=($(compgen -W "${CONFIG_CMDS.join(' ')}" -- "$cur")) ;;
|
|
51
|
+
*) COMPREPLY=($(compgen -W "$commands" -- "$cur")) ;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
return 0
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
complete -F _recall_completion recall`;
|
|
58
|
+
}
|
|
59
|
+
function zshCompletion() {
|
|
60
|
+
return `# recall zsh completion
|
|
61
|
+
# Add this to ~/.zshrc:
|
|
62
|
+
# eval "$(recall completion zsh)"
|
|
63
|
+
|
|
64
|
+
_recall() {
|
|
65
|
+
local state
|
|
66
|
+
|
|
67
|
+
_arguments \\
|
|
68
|
+
'1: :->command' \\
|
|
69
|
+
'*: :->args'
|
|
70
|
+
|
|
71
|
+
case $state in
|
|
72
|
+
command)
|
|
73
|
+
local commands=(${ALL_COMMANDS.map(c => `'${c}'`).join(' ')})
|
|
74
|
+
_describe 'command' commands
|
|
75
|
+
;;
|
|
76
|
+
args)
|
|
77
|
+
case $words[2] in
|
|
78
|
+
auth) local sub=(${AUTH_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
79
|
+
group) local sub=(${GROUP_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
80
|
+
org) local sub=(${ORG_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
81
|
+
alias) local sub=(${ALIAS_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
82
|
+
share) local sub=(${SHARE_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
83
|
+
snippet) local sub=(${SNIPPET_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
84
|
+
env) local sub=(${ENV_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
85
|
+
template) local sub=(${TEMPLATE_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
86
|
+
approve) local sub=(${APPROVE_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
87
|
+
config) local sub=(${CONFIG_CMDS.map(c => `'${c}'`).join(' ')}) && _describe 'subcommand' sub ;;
|
|
88
|
+
esac
|
|
89
|
+
;;
|
|
90
|
+
esac
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
compdef _recall recall`;
|
|
94
|
+
}
|
|
95
|
+
function psCompletion() {
|
|
96
|
+
return `# recall PowerShell completion
|
|
97
|
+
# Add this to your PowerShell profile ($PROFILE):
|
|
98
|
+
# recall completion ps | Out-String | Invoke-Expression
|
|
99
|
+
|
|
100
|
+
Register-ArgumentCompleter -Native -CommandName recall -ScriptBlock {
|
|
101
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
102
|
+
|
|
103
|
+
$commands = @(${ALL_COMMANDS.map(c => `'${c}'`).join(', ')})
|
|
104
|
+
$tokens = $commandAst.CommandElements
|
|
105
|
+
|
|
106
|
+
$subCommands = @{
|
|
107
|
+
'auth' = @(${AUTH_CMDS.map(c => `'${c}'`).join(', ')})
|
|
108
|
+
'group' = @(${GROUP_CMDS.map(c => `'${c}'`).join(', ')})
|
|
109
|
+
'org' = @(${ORG_CMDS.map(c => `'${c}'`).join(', ')})
|
|
110
|
+
'alias' = @(${ALIAS_CMDS.map(c => `'${c}'`).join(', ')})
|
|
111
|
+
'share' = @(${SHARE_CMDS.map(c => `'${c}'`).join(', ')})
|
|
112
|
+
'snippet' = @(${SNIPPET_CMDS.map(c => `'${c}'`).join(', ')})
|
|
113
|
+
'env' = @(${ENV_CMDS.map(c => `'${c}'`).join(', ')})
|
|
114
|
+
'template' = @(${TEMPLATE_CMDS.map(c => `'${c}'`).join(', ')})
|
|
115
|
+
'approve' = @(${APPROVE_CMDS.map(c => `'${c}'`).join(', ')})
|
|
116
|
+
'config' = @(${CONFIG_CMDS.map(c => `'${c}'`).join(', ')})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if ($tokens.Count -ge 2) {
|
|
120
|
+
$mainCmd = $tokens[1].Value
|
|
121
|
+
if ($subCommands.ContainsKey($mainCmd)) {
|
|
122
|
+
$subCommands[$mainCmd] | Where-Object { $_ -like "$wordToComplete*" } |
|
|
123
|
+
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
$commands | Where-Object { $_ -like "$wordToComplete*" } |
|
|
129
|
+
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
130
|
+
}`;
|
|
131
|
+
}
|
|
132
|
+
exports.completionCommand = new commander_1.Command('completion')
|
|
133
|
+
.description('Generate shell completion script')
|
|
134
|
+
.argument('<shell>', 'Shell type: bash, zsh, or ps')
|
|
135
|
+
.action((shell) => {
|
|
136
|
+
switch (shell.toLowerCase()) {
|
|
137
|
+
case 'bash':
|
|
138
|
+
console.log(bashCompletion());
|
|
139
|
+
console.log(chalk_1.default.dim('\n# Install: add this to ~/.bashrc'));
|
|
140
|
+
console.log(chalk_1.default.dim('# eval "$(recall completion bash)"'));
|
|
141
|
+
break;
|
|
142
|
+
case 'zsh':
|
|
143
|
+
console.log(zshCompletion());
|
|
144
|
+
console.log(chalk_1.default.dim('\n# Install: add this to ~/.zshrc'));
|
|
145
|
+
console.log(chalk_1.default.dim('# eval "$(recall completion zsh)"'));
|
|
146
|
+
break;
|
|
147
|
+
case 'ps':
|
|
148
|
+
case 'powershell':
|
|
149
|
+
console.log(psCompletion());
|
|
150
|
+
console.log(chalk_1.default.dim('\n# Install: add this to your PowerShell profile'));
|
|
151
|
+
console.log(chalk_1.default.dim('# recall completion ps | Out-String | Invoke-Expression'));
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
console.error(chalk_1.default.red(`\n Unknown shell: ${shell}`));
|
|
155
|
+
console.log(chalk_1.default.dim(' Supported: bash, zsh, ps\n'));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
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.configCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
const config_1 = require("../lib/config");
|
|
11
|
+
const configShow = new commander_1.Command('show')
|
|
12
|
+
.description('Show current CLI config')
|
|
13
|
+
.action(() => {
|
|
14
|
+
const config = (0, config_1.getConfig)();
|
|
15
|
+
console.log();
|
|
16
|
+
console.log(` ${chalk_1.default.dim('Environment:')} ${chalk_1.default.cyan(config.env ?? 'custom')}`);
|
|
17
|
+
console.log(` ${chalk_1.default.dim('API URL:')} ${chalk_1.default.white(config.apiUrl)}`);
|
|
18
|
+
console.log(` ${chalk_1.default.dim('Username:')} ${chalk_1.default.white(config.username ?? '—')}`);
|
|
19
|
+
console.log();
|
|
20
|
+
});
|
|
21
|
+
const configSet = new commander_1.Command('set')
|
|
22
|
+
.description('Set a config value')
|
|
23
|
+
.argument('<key>', 'Config key: apiUrl')
|
|
24
|
+
.argument('<value>', 'Config value')
|
|
25
|
+
.action((key, value) => {
|
|
26
|
+
if (key !== 'apiUrl') {
|
|
27
|
+
console.error(chalk_1.default.red(`\n Unknown config key: ${key}\n`));
|
|
28
|
+
console.log(chalk_1.default.dim(' Available keys: apiUrl\n'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
(0, config_1.saveConfig)({ [key]: value, env: 'custom' });
|
|
32
|
+
console.log(chalk_1.default.green(`\n Set ${key} = ${value}\n`));
|
|
33
|
+
});
|
|
34
|
+
const configSwitch = new commander_1.Command('switch')
|
|
35
|
+
.description('Switch between environments')
|
|
36
|
+
.action(async () => {
|
|
37
|
+
const config = (0, config_1.getConfig)();
|
|
38
|
+
const choices = [
|
|
39
|
+
...Object.entries(config_1.ENVIRONMENTS).map(([name, url]) => ({
|
|
40
|
+
name: `${name.padEnd(12)} ${chalk_1.default.dim(url)}`,
|
|
41
|
+
value: name,
|
|
42
|
+
})),
|
|
43
|
+
{ name: 'custom (enter URL manually)', value: 'custom' },
|
|
44
|
+
];
|
|
45
|
+
const { env } = await inquirer_1.default.prompt([{
|
|
46
|
+
type: 'list',
|
|
47
|
+
name: 'env',
|
|
48
|
+
message: 'Switch to which environment?',
|
|
49
|
+
choices,
|
|
50
|
+
default: config.env ?? 'production',
|
|
51
|
+
}]);
|
|
52
|
+
if (env === 'custom') {
|
|
53
|
+
const { url } = await inquirer_1.default.prompt([{
|
|
54
|
+
type: 'input',
|
|
55
|
+
name: 'url',
|
|
56
|
+
message: 'Enter API URL:',
|
|
57
|
+
default: config.apiUrl,
|
|
58
|
+
validate: (v) => v.startsWith('http') || 'Must start with http:// or https://',
|
|
59
|
+
}]);
|
|
60
|
+
(0, config_1.saveConfig)({ apiUrl: url, env: 'custom' });
|
|
61
|
+
console.log(chalk_1.default.green(`\n Switched to custom: ${url}\n`));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
(0, config_1.saveConfig)({ apiUrl: config_1.ENVIRONMENTS[env], env });
|
|
65
|
+
console.log(chalk_1.default.green(`\n Switched to ${env}: ${config_1.ENVIRONMENTS[env]}\n`));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const configReset = new commander_1.Command('reset')
|
|
69
|
+
.description('Reset config to defaults')
|
|
70
|
+
.action(async () => {
|
|
71
|
+
const { confirm } = await inquirer_1.default.prompt([{
|
|
72
|
+
type: 'confirm',
|
|
73
|
+
name: 'confirm',
|
|
74
|
+
message: 'Reset all config to defaults?',
|
|
75
|
+
default: false,
|
|
76
|
+
}]);
|
|
77
|
+
if (!confirm) {
|
|
78
|
+
console.log(chalk_1.default.dim('\n Cancelled.\n'));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
(0, config_1.saveConfig)({ apiUrl: config_1.ENVIRONMENTS.production, env: 'production', username: undefined });
|
|
82
|
+
console.log(chalk_1.default.green('\n Config reset to production defaults.\n'));
|
|
83
|
+
});
|
|
84
|
+
exports.configCommand = new commander_1.Command('config')
|
|
85
|
+
.description('Manage CLI configuration')
|
|
86
|
+
.addCommand(configShow.name('dump'))
|
|
87
|
+
.addCommand(configSet.name('put'))
|
|
88
|
+
.addCommand(configSwitch.name('env'))
|
|
89
|
+
.addCommand(configReset.name('flush'));
|
|
@@ -0,0 +1,58 @@
|
|
|
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.deleteCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const api_1 = require("../lib/api");
|
|
12
|
+
const auth_1 = require("../lib/auth");
|
|
13
|
+
exports.deleteCommand = new commander_1.Command('delete')
|
|
14
|
+
.description('Delete a saved command by name')
|
|
15
|
+
.argument('<name>', 'Command name to delete')
|
|
16
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
17
|
+
.action(async (name, opts) => {
|
|
18
|
+
const token = await (0, auth_1.requireAuth)();
|
|
19
|
+
// First look up the command to confirm it exists
|
|
20
|
+
const spinner = (0, ora_1.default)('Looking up...').start();
|
|
21
|
+
let cmd;
|
|
22
|
+
try {
|
|
23
|
+
cmd = await api_1.ApiClient.get(`/commands/by-name/${encodeURIComponent(name)}`, token);
|
|
24
|
+
spinner.stop();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
spinner.fail(chalk_1.default.red(`Command "${name}" not found`));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
// Show what will be deleted
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(` ${chalk_1.default.dim('Name:')} ${chalk_1.default.white(cmd.name ?? '—')}`);
|
|
33
|
+
console.log(` ${chalk_1.default.dim('Command:')} ${chalk_1.default.white(cmd.command)}`);
|
|
34
|
+
console.log();
|
|
35
|
+
// Confirm unless --force
|
|
36
|
+
if (!opts.force) {
|
|
37
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: 'confirm',
|
|
40
|
+
name: 'confirm',
|
|
41
|
+
message: chalk_1.default.red(`Delete "${name}"?`),
|
|
42
|
+
default: false,
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
if (!confirm) {
|
|
46
|
+
console.log(chalk_1.default.dim('\n Cancelled.\n'));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const deleteSpinner = (0, ora_1.default)('Deleting...').start();
|
|
51
|
+
try {
|
|
52
|
+
await api_1.ApiClient.delete(`/commands/${cmd.id}`, token);
|
|
53
|
+
deleteSpinner.succeed(chalk_1.default.green(`Deleted "${name}"`));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
deleteSpinner.fail(chalk_1.default.red('Failed to delete'));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
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.doctorCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const api_1 = require("../lib/api");
|
|
10
|
+
const auth_1 = require("../lib/auth");
|
|
11
|
+
const config_1 = require("../lib/config");
|
|
12
|
+
const CHECK = chalk_1.default.green('✔');
|
|
13
|
+
const FAIL = chalk_1.default.red('✖');
|
|
14
|
+
const WARN = chalk_1.default.yellow('!');
|
|
15
|
+
async function check(label, fn) {
|
|
16
|
+
try {
|
|
17
|
+
const result = await fn();
|
|
18
|
+
const icon = result.ok ? CHECK : FAIL;
|
|
19
|
+
console.log(` ${icon} ${label.padEnd(28)} ${result.ok ? chalk_1.default.dim(result.info) : chalk_1.default.red(result.info)}`);
|
|
20
|
+
return result.ok;
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.log(` ${FAIL} ${label.padEnd(28)} ${chalk_1.default.red(err.message ?? 'failed')}`);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.doctorCommand = new commander_1.Command('doctor')
|
|
28
|
+
.description('Check your recall setup and connectivity')
|
|
29
|
+
.action(async () => {
|
|
30
|
+
console.log(chalk_1.default.bold('\n recall doctor\n'));
|
|
31
|
+
const config = (0, config_1.getConfig)();
|
|
32
|
+
const version = config_1.APP_VERSION;
|
|
33
|
+
let allOk = true;
|
|
34
|
+
// 1. Config file
|
|
35
|
+
const configOk = await check('Config file', async () => ({
|
|
36
|
+
ok: true,
|
|
37
|
+
info: `API URL: ${config.apiUrl}`,
|
|
38
|
+
}));
|
|
39
|
+
if (!configOk)
|
|
40
|
+
allOk = false;
|
|
41
|
+
// 2. API reachable
|
|
42
|
+
const apiOk = await check('API reachable', async () => {
|
|
43
|
+
const res = await fetch(`${config.apiUrl}/health`);
|
|
44
|
+
const data = await res.json();
|
|
45
|
+
return {
|
|
46
|
+
ok: data.status === 'ok',
|
|
47
|
+
info: `${config.apiUrl}/health`,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
if (!apiOk)
|
|
51
|
+
allOk = false;
|
|
52
|
+
// 3. Token exists
|
|
53
|
+
const token = await (0, auth_1.getToken)();
|
|
54
|
+
const tokenOk = await check('Auth token', async () => ({
|
|
55
|
+
ok: !!token,
|
|
56
|
+
info: token ? 'Found in keychain' : 'Not found — run: recall auth login',
|
|
57
|
+
}));
|
|
58
|
+
if (!tokenOk)
|
|
59
|
+
allOk = false;
|
|
60
|
+
// 4. Token valid
|
|
61
|
+
if (token) {
|
|
62
|
+
const authOk = await check('Token valid', async () => {
|
|
63
|
+
const { user } = await api_1.ApiClient.get('/auth/me', token);
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
info: `${user.username} — ${user.plan} plan`,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
if (!authOk)
|
|
70
|
+
allOk = false;
|
|
71
|
+
// 5. Plan info
|
|
72
|
+
await check('Plan', async () => {
|
|
73
|
+
const { user, stats } = await api_1.ApiClient.get('/auth/me', token);
|
|
74
|
+
return {
|
|
75
|
+
ok: true,
|
|
76
|
+
info: `${user.plan} — ${stats.commandCount} commands saved`,
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// 6. Version
|
|
81
|
+
await check('CLI version', async () => ({
|
|
82
|
+
ok: true,
|
|
83
|
+
info: `v${version}`,
|
|
84
|
+
}));
|
|
85
|
+
// 7. Environment
|
|
86
|
+
await check('Environment', async () => {
|
|
87
|
+
const isLocal = config.apiUrl.includes('127.0.0.1') ||
|
|
88
|
+
config.apiUrl.includes('localhost');
|
|
89
|
+
return {
|
|
90
|
+
ok: true,
|
|
91
|
+
info: isLocal ? 'local' : 'production',
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
console.log();
|
|
95
|
+
if (allOk) {
|
|
96
|
+
console.log(chalk_1.default.green(' Everything looks good!\n'));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.log(chalk_1.default.yellow(' Some checks failed. See above for details.\n'));
|
|
100
|
+
console.log(chalk_1.default.dim(' Common fixes:'));
|
|
101
|
+
console.log(chalk_1.default.dim(' · Not logged in? run: recall auth login'));
|
|
102
|
+
console.log(chalk_1.default.dim(' · API not running? cd packages/api && pnpm dev'));
|
|
103
|
+
console.log(chalk_1.default.dim(' · Wrong API URL? run: recall config switch'));
|
|
104
|
+
console.log();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
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.envCommand = 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 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
|
+
const envSave = new commander_1.Command('save')
|
|
15
|
+
.argument('<name>', 'Environment set name')
|
|
16
|
+
.description('Save a set of environment variables')
|
|
17
|
+
.action(async (name) => {
|
|
18
|
+
const token = await (0, auth_1.requireAuth)();
|
|
19
|
+
console.log(chalk_1.default.bold(`\n Save env set: ${name}\n`));
|
|
20
|
+
console.log(chalk_1.default.dim(' Enter variables one by one. Leave name empty to finish.\n'));
|
|
21
|
+
const vars = {};
|
|
22
|
+
while (true) {
|
|
23
|
+
const { key } = await inquirer_1.default.prompt([{
|
|
24
|
+
type: 'input',
|
|
25
|
+
name: 'key',
|
|
26
|
+
message: 'Variable name (empty to finish):',
|
|
27
|
+
}]);
|
|
28
|
+
if (!key.trim())
|
|
29
|
+
break;
|
|
30
|
+
const { value } = await inquirer_1.default.prompt([{
|
|
31
|
+
type: 'input',
|
|
32
|
+
name: 'value',
|
|
33
|
+
message: `Value for ${key}:`,
|
|
34
|
+
}]);
|
|
35
|
+
vars[key] = value;
|
|
36
|
+
}
|
|
37
|
+
if (!Object.keys(vars).length) {
|
|
38
|
+
console.log(chalk_1.default.dim('\n No variables entered. Cancelled.\n'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const spinner = (0, ora_1.default)('Saving...').start();
|
|
42
|
+
try {
|
|
43
|
+
await api_1.ApiClient.post('/env', { name, vars }, token);
|
|
44
|
+
spinner.succeed(chalk_1.default.green(`Saved env set "${name}" with ${Object.keys(vars).length} variables`));
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
spinner.fail(chalk_1.default.red(err.message ?? 'Failed to save'));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const envList = new commander_1.Command('list')
|
|
51
|
+
.description('List all your env sets')
|
|
52
|
+
.action(async () => {
|
|
53
|
+
const token = await (0, auth_1.requireAuth)();
|
|
54
|
+
const spinner = (0, ora_1.default)('Fetching...').start();
|
|
55
|
+
try {
|
|
56
|
+
const { envSets } = await api_1.ApiClient.get('/env', token);
|
|
57
|
+
spinner.stop();
|
|
58
|
+
if (!envSets.length) {
|
|
59
|
+
console.log(chalk_1.default.dim('\n No env sets yet. Run: recall env save <name>\n'));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const table = new cli_table3_1.default({
|
|
63
|
+
head: [chalk_1.default.cyan('Name'), chalk_1.default.cyan('Created')],
|
|
64
|
+
style: { head: [], border: ['grey'] },
|
|
65
|
+
colWidths: [30, 20],
|
|
66
|
+
});
|
|
67
|
+
for (const e of envSets) {
|
|
68
|
+
table.push([chalk_1.default.white(e.name), new Date(e.createdAt).toLocaleDateString()]);
|
|
69
|
+
}
|
|
70
|
+
console.log(table.toString());
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
spinner.fail(chalk_1.default.red('Failed to fetch env sets'));
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
const envShow = new commander_1.Command('show')
|
|
77
|
+
.argument('<name>', 'Env set name')
|
|
78
|
+
.description('Show all variables in an env set')
|
|
79
|
+
.action(async (name) => {
|
|
80
|
+
const token = await (0, auth_1.requireAuth)();
|
|
81
|
+
const spinner = (0, ora_1.default)('Fetching...').start();
|
|
82
|
+
try {
|
|
83
|
+
const envSet = await api_1.ApiClient.get(`/env/${encodeURIComponent(name)}`, token);
|
|
84
|
+
spinner.stop();
|
|
85
|
+
console.log(chalk_1.default.bold(`\n ${envSet.name}\n`));
|
|
86
|
+
for (const [key, value] of Object.entries(envSet.vars)) {
|
|
87
|
+
console.log(` ${chalk_1.default.cyan(key)}=${chalk_1.default.white(String(value))}`);
|
|
88
|
+
}
|
|
89
|
+
console.log();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
spinner.fail(chalk_1.default.red(`Env set "${name}" not found`));
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const envUse = new commander_1.Command('use')
|
|
96
|
+
.argument('<name>', 'Env set name')
|
|
97
|
+
.description('Print export commands to load env set into your shell')
|
|
98
|
+
.action(async (name) => {
|
|
99
|
+
const token = await (0, auth_1.requireAuth)();
|
|
100
|
+
const spinner = (0, ora_1.default)('Fetching...').start();
|
|
101
|
+
try {
|
|
102
|
+
const envSet = await api_1.ApiClient.get(`/env/${encodeURIComponent(name)}`, token);
|
|
103
|
+
spinner.stop();
|
|
104
|
+
console.log(chalk_1.default.dim(`\n # Run this to load "${name}" into your shell:`));
|
|
105
|
+
console.log(chalk_1.default.dim(' # eval $(recall env use ' + name + ')\n'));
|
|
106
|
+
for (const [key, value] of Object.entries(envSet.vars)) {
|
|
107
|
+
console.log(`export ${key}="${value}"`);
|
|
108
|
+
}
|
|
109
|
+
console.log();
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
spinner.fail(chalk_1.default.red(`Env set "${name}" not found`));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const envDelete = new commander_1.Command('delete')
|
|
116
|
+
.argument('<name>', 'Env set name')
|
|
117
|
+
.description('Delete an env set')
|
|
118
|
+
.action(async (name) => {
|
|
119
|
+
const token = await (0, auth_1.requireAuth)();
|
|
120
|
+
const spinner = (0, ora_1.default)('Deleting...').start();
|
|
121
|
+
try {
|
|
122
|
+
await api_1.ApiClient.delete(`/env/${encodeURIComponent(name)}`, token);
|
|
123
|
+
spinner.succeed(chalk_1.default.green(`Deleted env set "${name}"`));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
spinner.fail(chalk_1.default.red(`Env set "${name}" not found`));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
exports.envCommand = new commander_1.Command('env')
|
|
130
|
+
.description('Save and use environment variable sets')
|
|
131
|
+
.addCommand(envSave)
|
|
132
|
+
.addCommand(envList)
|
|
133
|
+
.addCommand(envShow)
|
|
134
|
+
.addCommand(envUse)
|
|
135
|
+
.addCommand(envDelete);
|
|
@@ -0,0 +1,68 @@
|
|
|
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.exportCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const api_1 = require("../lib/api");
|
|
13
|
+
const auth_1 = require("../lib/auth");
|
|
14
|
+
exports.exportCommand = new commander_1.Command('export')
|
|
15
|
+
.description('Export your saved commands')
|
|
16
|
+
.option('-f, --format <format>', 'Export format: json or sh', 'json')
|
|
17
|
+
.option('-o, --output <file>', 'Output file path')
|
|
18
|
+
.action(async (opts) => {
|
|
19
|
+
const token = await (0, auth_1.requireAuth)();
|
|
20
|
+
const spinner = (0, ora_1.default)('Fetching commands...').start();
|
|
21
|
+
try {
|
|
22
|
+
const { commands } = await api_1.ApiClient.get('/commands', token);
|
|
23
|
+
spinner.stop();
|
|
24
|
+
if (!commands.length) {
|
|
25
|
+
console.log(chalk_1.default.dim('\n No commands to export.\n'));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const format = opts.format.toLowerCase();
|
|
29
|
+
if (format !== 'json' && format !== 'sh') {
|
|
30
|
+
console.error(chalk_1.default.red('\n Invalid format. Use: json or sh\n'));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
let output = '';
|
|
34
|
+
if (format === 'json') {
|
|
35
|
+
output = JSON.stringify(commands, null, 2);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Shell script format
|
|
39
|
+
const lines = [
|
|
40
|
+
'#!/bin/sh',
|
|
41
|
+
'# recall export — generated ' + new Date().toISOString(),
|
|
42
|
+
'# Run any of these commands by copy-pasting or sourcing this file',
|
|
43
|
+
'',
|
|
44
|
+
];
|
|
45
|
+
for (const cmd of commands) {
|
|
46
|
+
if (cmd.name)
|
|
47
|
+
lines.push(`# ${cmd.name}`);
|
|
48
|
+
if (cmd.tags?.length)
|
|
49
|
+
lines.push(`# tags: ${cmd.tags.join(', ')}`);
|
|
50
|
+
lines.push(cmd.command);
|
|
51
|
+
lines.push('');
|
|
52
|
+
}
|
|
53
|
+
output = lines.join('\n');
|
|
54
|
+
}
|
|
55
|
+
// Write to file or stdout
|
|
56
|
+
if (opts.output) {
|
|
57
|
+
const filePath = path_1.default.resolve(opts.output);
|
|
58
|
+
fs_1.default.writeFileSync(filePath, output, 'utf-8');
|
|
59
|
+
console.log(chalk_1.default.green(`\n Exported ${commands.length} commands to ${filePath}\n`));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(output);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
spinner.fail(chalk_1.default.red('Export failed'));
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
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.feedbackCommand = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const open_1 = __importDefault(require("open"));
|
|
10
|
+
exports.feedbackCommand = new commander_1.Command('feedback')
|
|
11
|
+
.description('Send feedback or report a bug')
|
|
12
|
+
.option('-b, --bug', 'Report a bug')
|
|
13
|
+
.option('-i, --idea', 'Suggest a feature')
|
|
14
|
+
.action(async (opts) => {
|
|
15
|
+
let url = 'https://devkitvault.com/recall/feedback';
|
|
16
|
+
if (opts.bug)
|
|
17
|
+
url += '?type=bug';
|
|
18
|
+
if (opts.idea)
|
|
19
|
+
url += '?type=idea';
|
|
20
|
+
console.log();
|
|
21
|
+
console.log(chalk_1.default.bold(' Opening feedback page...'));
|
|
22
|
+
console.log(chalk_1.default.dim(` ${url}`));
|
|
23
|
+
console.log();
|
|
24
|
+
await (0, open_1.default)(url);
|
|
25
|
+
});
|