@devkitvault/recall 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +83 -81
- package/dist/commands/ask.js +100 -0
- package/dist/commands/auth/login.js +11 -7
- package/dist/commands/completion.js +98 -98
- package/dist/commands/upgrade.js +9 -9
- package/dist/index.js +26 -2
- package/dist/lib/auth.js +56 -17
- package/dist/lib/config.js +1 -1
- package/dist/lib/events.js +40 -0
- package/dist/lib/runtime.js +31 -0
- package/package.json +57 -56
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) devkitvault
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) devkitvault
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,81 +1,83 @@
|
|
|
1
|
-
# @devkitvault/recall
|
|
2
|
-
|
|
3
|
-
**recall** is a
|
|
4
|
-
|
|
5
|
-
Works with [recall.devkitvault.com](https://recall.devkitvault.com) and the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=devkitvault.recall-cmd).
|
|
6
|
-
|
|
7
|
-
## Requirements
|
|
8
|
-
|
|
9
|
-
- [Node.js](https://nodejs.org/) **18 or newer**
|
|
10
|
-
|
|
11
|
-
## Install
|
|
12
|
-
|
|
13
|
-
```sh
|
|
14
|
-
npm install -g @devkitvault/recall
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
recall --version
|
|
21
|
-
recall doctor
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Quick start
|
|
25
|
-
|
|
26
|
-
```sh
|
|
27
|
-
recall auth login
|
|
28
|
-
recall
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
- **
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
recall
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
# @devkitvault/recall
|
|
2
|
+
|
|
3
|
+
**recall** is an AI terminal copilot with a personal command vault. Ask in English. It prints a command. It does not run it. Saved commands can answer first so you do not spend an AI request.
|
|
4
|
+
|
|
5
|
+
Works with [recall.devkitvault.com](https://recall.devkitvault.com) and the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=devkitvault.recall-cmd).
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- [Node.js](https://nodejs.org/) **18 or newer**
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install -g @devkitvault/recall
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`rec` is the same command as `recall`.
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
recall --version
|
|
21
|
+
recall doctor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
recall auth login
|
|
28
|
+
recall "find the 10 biggest files in this directory"
|
|
29
|
+
rec "that docker command from last week"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Ask prints a suggestion (`source` is `vault`, `cache`, or `llm`). Copy or save it yourself.
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
recall save "docker compose up -d" --name docker --tags docker
|
|
36
|
+
recall run docker
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Run `recall --help` for the full command list. Free plans include **20 AI asks / month**; Pro includes **500**.
|
|
40
|
+
|
|
41
|
+
## Authentication
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
recall auth login
|
|
45
|
+
recall auth logout
|
|
46
|
+
recall whoami
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Prefer a standalone binary?
|
|
50
|
+
|
|
51
|
+
- **macOS / Linux:** `curl -fsSL https://devkitvault.com/recall/install.sh | sh`
|
|
52
|
+
- **Windows:** `irm https://devkitvault.com/recall/install.ps1 | iex`
|
|
53
|
+
|
|
54
|
+
See [recall.devkitvault.com/docs/install](https://recall.devkitvault.com/docs/install).
|
|
55
|
+
|
|
56
|
+
## Update
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
npm update -g @devkitvault/recall
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Troubleshooting
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
recall doctor
|
|
66
|
+
recall whoami
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If something still fails, open an issue on [GitHub](https://github.com/devkitvault/recall/issues) or visit [recall.devkitvault.com](https://recall.devkitvault.com).
|
|
70
|
+
|
|
71
|
+
## Links
|
|
72
|
+
|
|
73
|
+
| | |
|
|
74
|
+
| --------- | ----------------------------------------------------------------------------------------- |
|
|
75
|
+
| Website | [devkitvault.com/recall](https://devkitvault.com) |
|
|
76
|
+
| Docs | [recall.devkitvault.com/docs](https://recall.devkitvault.com/docs) |
|
|
77
|
+
| Dashboard | [recall.devkitvault.com](https://recall.devkitvault.com) |
|
|
78
|
+
| VS Code | [Marketplace](https://marketplace.visualstudio.com/items?itemName=devkitvault.recall-cmd) |
|
|
79
|
+
| Issues | [github.com/devkitvault/recall](https://github.com/devkitvault/recall/issues) |
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT — see [LICENSE](./LICENSE). © [devkitvault](https://devkitvault.com)
|
|
@@ -0,0 +1,100 @@
|
|
|
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.askCommand = void 0;
|
|
7
|
+
exports.runAsk = runAsk;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
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 events_1 = require("../lib/events");
|
|
15
|
+
const runtime_1 = require("../lib/runtime");
|
|
16
|
+
function riskColor(risk) {
|
|
17
|
+
if (risk === 'DANGEROUS')
|
|
18
|
+
return chalk_1.default.red(risk);
|
|
19
|
+
if (risk === 'CAUTION')
|
|
20
|
+
return chalk_1.default.yellow(risk);
|
|
21
|
+
return chalk_1.default.green(risk);
|
|
22
|
+
}
|
|
23
|
+
async function runAsk(query) {
|
|
24
|
+
const token = await (0, auth_1.requireAuth)();
|
|
25
|
+
const runtime = (0, runtime_1.detectRuntime)();
|
|
26
|
+
const spinner = (0, ora_1.default)('Looking up...').start();
|
|
27
|
+
try {
|
|
28
|
+
const result = await api_1.ApiClient.post('/ai/resolve', {
|
|
29
|
+
query,
|
|
30
|
+
os: runtime.os,
|
|
31
|
+
shell: runtime.shell,
|
|
32
|
+
mode: 'ask',
|
|
33
|
+
}, token);
|
|
34
|
+
spinner.stop();
|
|
35
|
+
console.log();
|
|
36
|
+
console.log(` ${chalk_1.default.white(result.command)}`);
|
|
37
|
+
if (result.explanation) {
|
|
38
|
+
console.log(` ${chalk_1.default.dim(result.explanation)}`);
|
|
39
|
+
}
|
|
40
|
+
console.log();
|
|
41
|
+
console.log(` ${chalk_1.default.dim('shell:')} ${result.shell} ${chalk_1.default.dim('risk:')} ${riskColor(result.risk)} ${chalk_1.default.dim('source:')} ${result.source}`);
|
|
42
|
+
if (result.source === 'vault') {
|
|
43
|
+
console.log(chalk_1.default.dim(' from your vault'));
|
|
44
|
+
}
|
|
45
|
+
if (result.remaining !== null) {
|
|
46
|
+
console.log(` ${chalk_1.default.dim(`ai left this month: ${result.remaining}`)}`);
|
|
47
|
+
}
|
|
48
|
+
if (result.alternatives?.length) {
|
|
49
|
+
console.log();
|
|
50
|
+
for (const alt of result.alternatives) {
|
|
51
|
+
console.log(` ${chalk_1.default.dim('alt')} ${alt}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(chalk_1.default.dim(' Ask mode does not run commands. Copy or save this yourself.'));
|
|
56
|
+
console.log();
|
|
57
|
+
if (result.source === 'vault') {
|
|
58
|
+
await (0, events_1.track)('ai_accepted', { source: result.source, risk: result.risk, saved: false });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const { save } = await inquirer_1.default.prompt([{
|
|
62
|
+
type: 'confirm',
|
|
63
|
+
name: 'save',
|
|
64
|
+
message: 'Save this command?',
|
|
65
|
+
default: false,
|
|
66
|
+
}]);
|
|
67
|
+
if (!save)
|
|
68
|
+
return;
|
|
69
|
+
const { name } = await inquirer_1.default.prompt([{
|
|
70
|
+
type: 'input',
|
|
71
|
+
name: 'name',
|
|
72
|
+
message: 'Name (optional):',
|
|
73
|
+
}]);
|
|
74
|
+
await api_1.ApiClient.post('/commands', {
|
|
75
|
+
command: result.command,
|
|
76
|
+
name: name?.trim() || undefined,
|
|
77
|
+
}, token);
|
|
78
|
+
await (0, events_1.track)('ai_accepted', { source: result.source, risk: result.risk, saved: true });
|
|
79
|
+
console.log(chalk_1.default.green('\n Saved.\n'));
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
spinner.stop();
|
|
83
|
+
if (err instanceof api_1.ApiError && err.status === 403 && err.body?.upgrade) {
|
|
84
|
+
console.log(chalk_1.default.yellow(`\n ${err.message}`));
|
|
85
|
+
console.log(chalk_1.default.dim(` Upgrade at ${chalk_1.default.white(err.body.upgrade)}\n`));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (err instanceof api_1.ApiError) {
|
|
89
|
+
console.error(chalk_1.default.red(`\n ${err.message}\n`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.error(chalk_1.default.red('\n Failed to resolve that request.\n'));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.askCommand = new commander_1.Command('ask')
|
|
96
|
+
.description('Suggest a command from natural language (does not run it)')
|
|
97
|
+
.argument('<query...>', 'What you want to do')
|
|
98
|
+
.action(async (parts) => {
|
|
99
|
+
await runAsk(parts.join(' '));
|
|
100
|
+
});
|
|
@@ -70,6 +70,8 @@ async function loginWithCredentials() {
|
|
|
70
70
|
(0, config_1.saveConfig)({ username });
|
|
71
71
|
spinner.succeed(chalk_1.default.green('Authenticated successfully!'));
|
|
72
72
|
console.log(chalk_1.default.dim(`\n Logged in as ${chalk_1.default.white(username)}\n`));
|
|
73
|
+
const { track } = await Promise.resolve().then(() => __importStar(require('../../lib/events')));
|
|
74
|
+
await track('cli_login', { method: 'credentials' });
|
|
73
75
|
}
|
|
74
76
|
catch (err) {
|
|
75
77
|
spinner.fail(chalk_1.default.red('Authentication failed'));
|
|
@@ -115,13 +117,13 @@ async function loginWithBrowser() {
|
|
|
115
117
|
}
|
|
116
118
|
// Loopback callback: tell the browser to close the socket so server.close() can finish promptly.
|
|
117
119
|
res.writeHead(200, { 'Content-Type': 'text/html', ...closeHeaders });
|
|
118
|
-
res.end(`<!DOCTYPE html>
|
|
119
|
-
<html>
|
|
120
|
-
<head><meta charset="utf-8"></head>
|
|
121
|
-
<body style="font-family:system-ui;max-width:480px;margin:80px auto;text-align:center;background:#080b0f;color:#e2e8f0;">
|
|
122
|
-
<h2 style="color:#4ade80;margin-bottom:12px;">✓ Authenticated!</h2>
|
|
123
|
-
<p style="color:#6b7e94;font-family:monospace;">You can close this tab and return to your terminal.</p>
|
|
124
|
-
</body>
|
|
120
|
+
res.end(`<!DOCTYPE html>
|
|
121
|
+
<html>
|
|
122
|
+
<head><meta charset="utf-8"></head>
|
|
123
|
+
<body style="font-family:system-ui;max-width:480px;margin:80px auto;text-align:center;background:#080b0f;color:#e2e8f0;">
|
|
124
|
+
<h2 style="color:#4ade80;margin-bottom:12px;">✓ Authenticated!</h2>
|
|
125
|
+
<p style="color:#6b7e94;font-family:monospace;">You can close this tab and return to your terminal.</p>
|
|
126
|
+
</body>
|
|
125
127
|
</html>`);
|
|
126
128
|
const srv = server;
|
|
127
129
|
srv.closeAllConnections?.();
|
|
@@ -145,6 +147,8 @@ async function loginWithBrowser() {
|
|
|
145
147
|
(0, config_1.saveConfig)({ username });
|
|
146
148
|
spinner.succeed(chalk_1.default.green('Authenticated successfully!'));
|
|
147
149
|
console.log(chalk_1.default.dim(`\n Logged in as ${chalk_1.default.white(username)}\n`));
|
|
150
|
+
const { track } = await Promise.resolve().then(() => __importStar(require('../../lib/events')));
|
|
151
|
+
await track('cli_login', { method: 'browser' });
|
|
148
152
|
}
|
|
149
153
|
exports.loginCommand = new commander_1.Command('login')
|
|
150
154
|
.description('Log in to recall')
|
|
@@ -11,7 +11,7 @@ const ALL_COMMANDS = [
|
|
|
11
11
|
'export', 'import', 'sync', 'whoami', 'upgrade', 'config',
|
|
12
12
|
'pin', 'alias', 'history', 'share', 'snippet', 'env',
|
|
13
13
|
'group', 'org', 'audit', 'approve', 'template', 'doctor',
|
|
14
|
-
'completion',
|
|
14
|
+
'completion', 'ask',
|
|
15
15
|
];
|
|
16
16
|
const AUTH_CMDS = ['register', 'login', 'logout', 'forgot', 'reset'];
|
|
17
17
|
const GROUP_CMDS = ['create', 'list', 'show', 'add', 'remove', 'delete', 'rename'];
|
|
@@ -24,109 +24,109 @@ const TEMPLATE_CMDS = ['save', 'list', 'show', 'run', 'delete'];
|
|
|
24
24
|
const APPROVE_CMDS = ['request', 'grant', 'list'];
|
|
25
25
|
const CONFIG_CMDS = ['show', 'set', 'switch', 'reset'];
|
|
26
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
|
-
|
|
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
57
|
complete -F _recall_completion recall`;
|
|
58
58
|
}
|
|
59
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
|
-
|
|
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
93
|
compdef _recall recall`;
|
|
94
94
|
}
|
|
95
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', $_) }
|
|
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
130
|
}`;
|
|
131
131
|
}
|
|
132
132
|
exports.completionCommand = new commander_1.Command('completion')
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -16,7 +16,8 @@ const PLANS = {
|
|
|
16
16
|
label: 'Free',
|
|
17
17
|
price: '$0/month',
|
|
18
18
|
features: [
|
|
19
|
-
'
|
|
19
|
+
'20 AI asks / month',
|
|
20
|
+
'100 command saves',
|
|
20
21
|
'Search and filter',
|
|
21
22
|
'Export as JSON or shell script',
|
|
22
23
|
'Cross-machine sync',
|
|
@@ -24,22 +25,21 @@ const PLANS = {
|
|
|
24
25
|
},
|
|
25
26
|
pro: {
|
|
26
27
|
label: 'Pro',
|
|
27
|
-
price: '$
|
|
28
|
+
price: '$6/month',
|
|
28
29
|
features: [
|
|
29
|
-
'
|
|
30
|
+
'500 AI asks / month',
|
|
31
|
+
'Unlimited commands',
|
|
30
32
|
'Command groups',
|
|
31
33
|
'Share commands via link',
|
|
32
|
-
'Priority support',
|
|
33
34
|
],
|
|
34
35
|
},
|
|
35
36
|
team: {
|
|
36
37
|
label: 'Team',
|
|
37
|
-
price: '$
|
|
38
|
+
price: '$12/month per seat',
|
|
38
39
|
features: [
|
|
39
40
|
'Everything in Pro',
|
|
40
41
|
'Org-level vaults',
|
|
41
42
|
'Role-based access',
|
|
42
|
-
'SSO / SAML',
|
|
43
43
|
'Audit log',
|
|
44
44
|
],
|
|
45
45
|
},
|
|
@@ -81,12 +81,12 @@ exports.upgradeCommand = new commander_1.Command('upgrade')
|
|
|
81
81
|
}
|
|
82
82
|
const upgradeTo = currentPlan === 'free'
|
|
83
83
|
? [
|
|
84
|
-
{ name: 'Upgrade to Pro — $
|
|
85
|
-
{ name: 'Upgrade to Team — $
|
|
84
|
+
{ name: 'Upgrade to Pro — $6/month', value: 'pro' },
|
|
85
|
+
{ name: 'Upgrade to Team — $12/month per seat', value: 'team' },
|
|
86
86
|
{ name: 'Maybe later', value: 'exit' },
|
|
87
87
|
]
|
|
88
88
|
: [
|
|
89
|
-
{ name: 'Upgrade to Team — $
|
|
89
|
+
{ name: 'Upgrade to Team — $12/month per seat', value: 'team' },
|
|
90
90
|
{ name: 'Maybe later', value: 'exit' },
|
|
91
91
|
];
|
|
92
92
|
const { choice } = await inquirer_1.default.prompt([
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
const commander_1 = require("commander");
|
|
9
9
|
const alias_1 = require("./commands/alias");
|
|
10
10
|
const approve_1 = require("./commands/approve");
|
|
11
|
+
const ask_1 = require("./commands/ask");
|
|
11
12
|
const audit_1 = require("./commands/audit");
|
|
12
13
|
const index_1 = require("./commands/auth/index");
|
|
13
14
|
const completion_1 = require("./commands/completion");
|
|
@@ -35,11 +36,26 @@ const update_1 = require("./commands/update");
|
|
|
35
36
|
const upgrade_1 = require("./commands/upgrade");
|
|
36
37
|
const whoami_1 = require("./commands/whoami");
|
|
37
38
|
const config_2 = require("./lib/config");
|
|
39
|
+
const events_1 = require("./lib/events");
|
|
40
|
+
const KNOWN = new Set([
|
|
41
|
+
'auth', 'save', 'list', 'run', 'delete', 'search', 'update',
|
|
42
|
+
'export', 'import', 'whoami', 'upgrade', 'sync', 'group', 'org',
|
|
43
|
+
'config', 'pin', 'alias', 'history', 'share', 'snippet', 'env',
|
|
44
|
+
'audit', 'approve', 'template', 'doctor', 'completion', 'feedback',
|
|
45
|
+
'support', 'ask', 'help',
|
|
46
|
+
]);
|
|
47
|
+
const rawArgs = process.argv.slice(2);
|
|
48
|
+
if (rawArgs.length > 0
|
|
49
|
+
&& !rawArgs[0].startsWith('-')
|
|
50
|
+
&& !KNOWN.has(rawArgs[0])) {
|
|
51
|
+
process.argv = [process.argv[0], process.argv[1], 'ask', ...rawArgs];
|
|
52
|
+
}
|
|
38
53
|
const program = new commander_1.Command();
|
|
39
54
|
program
|
|
40
55
|
.name('recall')
|
|
41
|
-
.description(chalk_1.default.cyan('
|
|
56
|
+
.description(chalk_1.default.cyan('Your terminal remembers how you work'))
|
|
42
57
|
.version(config_2.APP_VERSION);
|
|
58
|
+
program.addCommand(ask_1.askCommand);
|
|
43
59
|
program.addCommand(index_1.authCommand);
|
|
44
60
|
program.addCommand(save_1.saveCommand);
|
|
45
61
|
program.addCommand(list_1.listCommand);
|
|
@@ -68,4 +84,12 @@ program.addCommand(doctor_1.doctorCommand);
|
|
|
68
84
|
program.addCommand(completion_1.completionCommand);
|
|
69
85
|
program.addCommand(feedback_1.feedbackCommand);
|
|
70
86
|
program.addCommand(support_1.supportCommand);
|
|
71
|
-
|
|
87
|
+
async function main() {
|
|
88
|
+
void (0, events_1.trackInstalledOnce)();
|
|
89
|
+
await program.parseAsync();
|
|
90
|
+
}
|
|
91
|
+
main().catch((err) => {
|
|
92
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
93
|
+
console.error(chalk_1.default.red(`\n ${message}\n`));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|
package/dist/lib/auth.js
CHANGED
|
@@ -40,42 +40,81 @@ exports.getToken = getToken;
|
|
|
40
40
|
exports.setToken = setToken;
|
|
41
41
|
exports.deleteToken = deleteToken;
|
|
42
42
|
exports.requireAuth = requireAuth;
|
|
43
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
44
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
45
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
43
46
|
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
-
const TOKEN_FILE =
|
|
45
|
-
async function
|
|
47
|
+
const TOKEN_FILE = node_path_1.default.join(node_os_1.default.homedir(), '.recall', 'token');
|
|
48
|
+
async function loadKeytar() {
|
|
49
|
+
try {
|
|
50
|
+
const loaded = await Promise.resolve().then(() => __importStar(require('keytar')));
|
|
51
|
+
const api = loaded.default ?? loaded;
|
|
52
|
+
if (typeof api.getPassword !== 'function' || typeof api.setPassword !== 'function') {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return api;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function readTokenFile() {
|
|
46
62
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
if (!node_fs_1.default.existsSync(TOKEN_FILE))
|
|
64
|
+
return null;
|
|
65
|
+
const token = node_fs_1.default.readFileSync(TOKEN_FILE, 'utf-8').trim();
|
|
66
|
+
return token || null;
|
|
49
67
|
}
|
|
50
68
|
catch {
|
|
51
|
-
// fallback to file if keytar not available
|
|
52
|
-
const fs = require('fs');
|
|
53
|
-
if (fs.existsSync(TOKEN_FILE))
|
|
54
|
-
return fs.readFileSync(TOKEN_FILE, 'utf-8').trim();
|
|
55
69
|
return null;
|
|
56
70
|
}
|
|
57
71
|
}
|
|
72
|
+
function writeTokenFile(token) {
|
|
73
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(TOKEN_FILE), { recursive: true });
|
|
74
|
+
node_fs_1.default.writeFileSync(TOKEN_FILE, token, { mode: 0o600 });
|
|
75
|
+
}
|
|
76
|
+
async function getToken() {
|
|
77
|
+
const keytar = await loadKeytar();
|
|
78
|
+
if (keytar) {
|
|
79
|
+
try {
|
|
80
|
+
const fromKeytar = await keytar.getPassword('recall', 'token');
|
|
81
|
+
if (fromKeytar)
|
|
82
|
+
return fromKeytar;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// fall through to file
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return readTokenFile();
|
|
89
|
+
}
|
|
58
90
|
async function setToken(token) {
|
|
91
|
+
writeTokenFile(token);
|
|
92
|
+
const keytar = await loadKeytar();
|
|
93
|
+
if (!keytar)
|
|
94
|
+
return;
|
|
59
95
|
try {
|
|
60
|
-
const keytar = await Promise.resolve().then(() => __importStar(require('keytar')));
|
|
61
96
|
await keytar.setPassword('recall', 'token', token);
|
|
62
97
|
}
|
|
63
98
|
catch {
|
|
64
|
-
|
|
65
|
-
const path = require('path');
|
|
66
|
-
fs.mkdirSync(path.dirname(TOKEN_FILE), { recursive: true });
|
|
67
|
-
fs.writeFileSync(TOKEN_FILE, token, { mode: 0o600 });
|
|
99
|
+
// file is already written
|
|
68
100
|
}
|
|
69
101
|
}
|
|
70
102
|
async function deleteToken() {
|
|
71
103
|
try {
|
|
72
|
-
|
|
104
|
+
if (node_fs_1.default.existsSync(TOKEN_FILE))
|
|
105
|
+
node_fs_1.default.unlinkSync(TOKEN_FILE);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// ignore
|
|
109
|
+
}
|
|
110
|
+
const keytar = await loadKeytar();
|
|
111
|
+
if (!keytar)
|
|
112
|
+
return;
|
|
113
|
+
try {
|
|
73
114
|
await keytar.deletePassword('recall', 'token');
|
|
74
115
|
}
|
|
75
116
|
catch {
|
|
76
|
-
|
|
77
|
-
if (fs.existsSync(TOKEN_FILE))
|
|
78
|
-
fs.unlinkSync(TOKEN_FILE);
|
|
117
|
+
// ignore
|
|
79
118
|
}
|
|
80
119
|
}
|
|
81
120
|
async function requireAuth() {
|
package/dist/lib/config.js
CHANGED
|
@@ -11,7 +11,7 @@ const os_1 = __importDefault(require("os"));
|
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.recall');
|
|
13
13
|
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
14
|
-
exports.APP_VERSION = "
|
|
14
|
+
exports.APP_VERSION = "2.5.0";
|
|
15
15
|
exports.ENVIRONMENTS = {
|
|
16
16
|
production: 'https://api.devkitvault.com',
|
|
17
17
|
local: 'http://127.0.0.1:3001',
|
|
@@ -0,0 +1,40 @@
|
|
|
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.track = track;
|
|
7
|
+
exports.trackInstalledOnce = trackInstalledOnce;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const api_1 = require("./api");
|
|
12
|
+
const auth_1 = require("./auth");
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const FLAG_FILE = path_1.default.join(os_1.default.homedir(), '.recall', 'installed');
|
|
15
|
+
function markInstalled() {
|
|
16
|
+
try {
|
|
17
|
+
if (fs_1.default.existsSync(FLAG_FILE))
|
|
18
|
+
return false;
|
|
19
|
+
fs_1.default.mkdirSync(path_1.default.dirname(FLAG_FILE), { recursive: true });
|
|
20
|
+
fs_1.default.writeFileSync(FLAG_FILE, new Date().toISOString(), { mode: 0o600 });
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function track(name, properties) {
|
|
28
|
+
try {
|
|
29
|
+
const token = await (0, auth_1.getToken)();
|
|
30
|
+
await api_1.ApiClient.post('/events', { name, properties: properties ?? {} }, token ?? undefined);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// never block the CLI on analytics
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function trackInstalledOnce() {
|
|
37
|
+
if (!markInstalled())
|
|
38
|
+
return;
|
|
39
|
+
await track('cli_installed', { version: config_1.APP_VERSION });
|
|
40
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectRuntime = detectRuntime;
|
|
4
|
+
function detectRuntime() {
|
|
5
|
+
const os = process.platform === 'win32'
|
|
6
|
+
? 'windows'
|
|
7
|
+
: process.platform === 'darwin'
|
|
8
|
+
? 'macos'
|
|
9
|
+
: 'linux';
|
|
10
|
+
if (os === 'windows') {
|
|
11
|
+
const shellEnv = (process.env.SHELL ?? '').toLowerCase();
|
|
12
|
+
if (shellEnv.includes('zsh'))
|
|
13
|
+
return { os, shell: 'zsh' };
|
|
14
|
+
if (shellEnv.includes('bash'))
|
|
15
|
+
return { os, shell: 'bash' };
|
|
16
|
+
if (process.env.PSModulePath)
|
|
17
|
+
return { os, shell: 'powershell' };
|
|
18
|
+
const comspec = (process.env.ComSpec ?? '').toLowerCase();
|
|
19
|
+
if (comspec.includes('cmd'))
|
|
20
|
+
return { os, shell: 'cmd' };
|
|
21
|
+
return { os, shell: 'powershell' };
|
|
22
|
+
}
|
|
23
|
+
const s = process.env.SHELL ?? '/bin/bash';
|
|
24
|
+
if (s.includes('zsh'))
|
|
25
|
+
return { os, shell: 'zsh' };
|
|
26
|
+
if (s.includes('fish'))
|
|
27
|
+
return { os, shell: 'fish' };
|
|
28
|
+
if (s.includes('pwsh') || s.includes('powershell'))
|
|
29
|
+
return { os, shell: 'powershell' };
|
|
30
|
+
return { os, shell: 'bash' };
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,56 +1,57 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@devkitvault/recall",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "recall CLI —
|
|
5
|
-
"keywords": [
|
|
6
|
-
"cli",
|
|
7
|
-
"shell",
|
|
8
|
-
"commands",
|
|
9
|
-
"devtools",
|
|
10
|
-
"recall",
|
|
11
|
-
"vault",
|
|
12
|
-
"sync",
|
|
13
|
-
"kubernetes",
|
|
14
|
-
"docker",
|
|
15
|
-
"terminal",
|
|
16
|
-
"devkitvault"
|
|
17
|
-
],
|
|
18
|
-
"author": "devkitvault",
|
|
19
|
-
"license": "MIT",
|
|
20
|
-
"homepage": "https://recall.devkitvault.com",
|
|
21
|
-
"bugs": {
|
|
22
|
-
"url": "https://github.com/devkitvault/recall/issues"
|
|
23
|
-
},
|
|
24
|
-
"repository": {
|
|
25
|
-
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/devkitvault/recall.git",
|
|
27
|
-
"directory": "packages/npm-cli"
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@devkitvault/recall",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "recall CLI — AI terminal copilot with a personal command vault",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"shell",
|
|
8
|
+
"commands",
|
|
9
|
+
"devtools",
|
|
10
|
+
"recall",
|
|
11
|
+
"vault",
|
|
12
|
+
"sync",
|
|
13
|
+
"kubernetes",
|
|
14
|
+
"docker",
|
|
15
|
+
"terminal",
|
|
16
|
+
"devkitvault"
|
|
17
|
+
],
|
|
18
|
+
"author": "devkitvault",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"homepage": "https://recall.devkitvault.com",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/devkitvault/recall/issues"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/devkitvault/recall.git",
|
|
27
|
+
"directory": "packages/npm-cli"
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"recall": "dist/index.js",
|
|
31
|
+
"rec": "dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18.0.0"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"prepublishOnly": "node scripts/prepublish.cjs && node --check dist/index.js",
|
|
46
|
+
"check:registry": "npm view @devkitvault/recall version",
|
|
47
|
+
"try:published": "npx --yes --package=@devkitvault/recall@latest recall --version"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"chalk": "^4.1.2",
|
|
51
|
+
"cli-table3": "^0.6.3",
|
|
52
|
+
"commander": "^12.0.0",
|
|
53
|
+
"inquirer": "^8.2.7",
|
|
54
|
+
"open": "^8.4.2",
|
|
55
|
+
"ora": "^5.4.1"
|
|
56
|
+
}
|
|
57
|
+
}
|