@devkitvault/recall 1.0.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 -0
- package/README.md +52 -0
- package/bin/recall.js +169 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +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.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @devkitvault/recall
|
|
2
|
+
|
|
3
|
+
**Install the recall CLI from npm** — on first run, this package downloads the official native binary for your OS from [GitHub Releases](https://github.com/devkitvault/recall/releases) into `~/.local/bin`, then runs it. Later runs reuse the same binary.
|
|
4
|
+
|
|
5
|
+
recall is a **cloud-synced command vault**: save shell commands, templates, snippets, env sets, and aliases — same account as [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 18+** (only for this installer wrapper)
|
|
10
|
+
- **Supported platforms** (prebuilt binaries): macOS (Apple Silicon + Intel), Linux x64, Windows x64
|
|
11
|
+
|
|
12
|
+
Other architectures: use the [curl / PowerShell installers](https://devkitvault.com) or build from source in `packages/cli`.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install -g @devkitvault/recall
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Ensure `~/.local/bin` is on your `PATH` (the installer places `recall` there). On Windows, that path is `%USERPROFILE%\.local\bin`.
|
|
21
|
+
|
|
22
|
+
## Use
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
recall --help
|
|
26
|
+
recall auth login
|
|
27
|
+
recall save "docker compose up -d" -n "start stack"
|
|
28
|
+
recall search docker
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Re-download the binary
|
|
32
|
+
|
|
33
|
+
If a release was updated or your download was corrupted:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
RECALL_REINSTALL=1 recall --version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Release tag vs npm version
|
|
40
|
+
|
|
41
|
+
Downloads use a Git **tag** on `devkitvault/recall` (default: `v` + `version` from this package’s `package.json`). If you ship an npm **patch** without a matching GitHub tag, set either:
|
|
42
|
+
|
|
43
|
+
- `recall.releaseTag` in `package.json`, or
|
|
44
|
+
- environment variable `RECALL_RELEASE_TAG` (e.g. `v1.0.0`)
|
|
45
|
+
|
|
46
|
+
before running `recall`, so the wrapper points at an existing release.
|
|
47
|
+
|
|
48
|
+
Maintainers: after [Release CLI](https://github.com/devkitvault/recall/blob/master/.github/workflows/release.yml) publishes assets for `vX.Y.Z`, publish this npm package with the same `version` **or** set `recall.releaseTag` accordingly.
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT © [devkitvault](https://devkitvault.com)
|
package/bin/recall.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @devkitvault/recall — thin installer for the native recall CLI.
|
|
4
|
+
* First run downloads the matching GitHub release binary into ~/.local/bin, then execs it.
|
|
5
|
+
* @see https://github.com/devkitvault/recall/releases
|
|
6
|
+
*/
|
|
7
|
+
'use strict'
|
|
8
|
+
|
|
9
|
+
const fs = require('fs')
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const os = require('os')
|
|
12
|
+
const https = require('https')
|
|
13
|
+
const http = require('http')
|
|
14
|
+
const { spawnSync } = require('child_process')
|
|
15
|
+
|
|
16
|
+
const BINARY_MAP = {
|
|
17
|
+
'darwin-arm64': 'recall-macos-arm64',
|
|
18
|
+
'darwin-x64': 'recall-macos-x64',
|
|
19
|
+
'linux-x64': 'recall-linux-x64',
|
|
20
|
+
'win32-x64': 'recall-windows-x64.exe',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const USER_AGENT = 'devkitvault-recall-npm/1.0 (+https://www.npmjs.com/package/@devkitvault/recall)'
|
|
24
|
+
|
|
25
|
+
function platformKey() {
|
|
26
|
+
return `${process.platform}-${process.arch}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function binaryAssetName() {
|
|
30
|
+
const key = platformKey()
|
|
31
|
+
const name = BINARY_MAP[key]
|
|
32
|
+
if (!name) {
|
|
33
|
+
console.error(
|
|
34
|
+
`recall: no prebuilt binary for "${key}".\n` +
|
|
35
|
+
`Supported: ${Object.keys(BINARY_MAP).join(', ')}\n` +
|
|
36
|
+
'Install from https://devkitvault.com or build from source (packages/cli).',
|
|
37
|
+
)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
return name
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readPkg() {
|
|
44
|
+
return require(path.join(__dirname, '..', 'package.json'))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function releaseTag(pkg) {
|
|
48
|
+
if (process.env.RECALL_RELEASE_TAG) {
|
|
49
|
+
const t = process.env.RECALL_RELEASE_TAG.trim()
|
|
50
|
+
return t.startsWith('v') ? t : `v${t}`
|
|
51
|
+
}
|
|
52
|
+
const t = pkg.recall && pkg.recall.releaseTag
|
|
53
|
+
if (typeof t === 'string' && t.length) return t.startsWith('v') ? t : `v${t}`
|
|
54
|
+
return `v${pkg.version}`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function binPaths() {
|
|
58
|
+
const binDir = path.join(os.homedir(), '.local', 'bin')
|
|
59
|
+
const binName = process.platform === 'win32' ? 'recall.exe' : 'recall'
|
|
60
|
+
return { binDir, binPath: path.join(binDir, binName) }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function httpGet(url, redirectsLeft) {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
if (redirectsLeft <= 0) return reject(new Error('Too many redirects'))
|
|
66
|
+
const lib = url.startsWith('https:') ? https : http
|
|
67
|
+
const req = lib.get(
|
|
68
|
+
url,
|
|
69
|
+
{
|
|
70
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
71
|
+
},
|
|
72
|
+
(res) => {
|
|
73
|
+
const loc = res.headers.location
|
|
74
|
+
if (loc && res.statusCode >= 300 && res.statusCode < 400) {
|
|
75
|
+
res.resume()
|
|
76
|
+
const next = new URL(loc, url).href
|
|
77
|
+
return httpGet(next, redirectsLeft - 1).then(resolve).catch(reject)
|
|
78
|
+
}
|
|
79
|
+
if (res.statusCode !== 200) {
|
|
80
|
+
res.resume()
|
|
81
|
+
return reject(new Error(`HTTP ${res.statusCode} GET ${url}`))
|
|
82
|
+
}
|
|
83
|
+
resolve(res)
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
req.on('error', reject)
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function streamToFile(readable, destPath) {
|
|
91
|
+
return fs.promises.mkdir(path.dirname(destPath), { recursive: true }).then(() => {
|
|
92
|
+
const tmp = `${destPath}.${process.pid}.${Date.now()}.part`
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
const w = fs.createWriteStream(tmp, { mode: 0o644 })
|
|
95
|
+
readable.pipe(w)
|
|
96
|
+
w.on('finish', () => w.close((err) => (err ? reject(err) : resolve(tmp))))
|
|
97
|
+
w.on('error', reject)
|
|
98
|
+
readable.on('error', reject)
|
|
99
|
+
}).then(async (tmpPath) => {
|
|
100
|
+
try {
|
|
101
|
+
await fs.promises.rename(tmpPath, destPath)
|
|
102
|
+
} catch (e) {
|
|
103
|
+
try {
|
|
104
|
+
await fs.promises.unlink(tmpPath)
|
|
105
|
+
} catch { /* ignore */ }
|
|
106
|
+
throw e
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function ensureBinary(binPath, tag, assetName) {
|
|
113
|
+
const url = `https://github.com/devkitvault/recall/releases/download/${tag}/${assetName}`
|
|
114
|
+
process.stderr.write(`recall: downloading ${assetName} (${tag}) …\n`)
|
|
115
|
+
const res = await httpGet(url, 16)
|
|
116
|
+
await streamToFile(res, binPath)
|
|
117
|
+
if (process.platform !== 'win32') {
|
|
118
|
+
await fs.promises.chmod(binPath, 0o755)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function main() {
|
|
123
|
+
const pkg = readPkg()
|
|
124
|
+
const tag = releaseTag(pkg)
|
|
125
|
+
const assetName = binaryAssetName()
|
|
126
|
+
const { binPath } = binPaths()
|
|
127
|
+
|
|
128
|
+
const reinstall =
|
|
129
|
+
process.env.RECALL_REINSTALL === '1' ||
|
|
130
|
+
process.env.FORCE_RECALL_REINSTALL === '1'
|
|
131
|
+
|
|
132
|
+
if (reinstall) {
|
|
133
|
+
try {
|
|
134
|
+
await fs.promises.unlink(binPath)
|
|
135
|
+
} catch { /* missing is fine */ }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let exists = false
|
|
139
|
+
try {
|
|
140
|
+
const st = await fs.promises.stat(binPath)
|
|
141
|
+
exists = st.isFile() && st.size > 0
|
|
142
|
+
} catch {
|
|
143
|
+
exists = false
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!exists) {
|
|
147
|
+
try {
|
|
148
|
+
await ensureBinary(binPath, tag, assetName)
|
|
149
|
+
} catch (err) {
|
|
150
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
151
|
+
console.error(`recall: install failed: ${msg}`)
|
|
152
|
+
console.error(`Expected release assets at: https://github.com/devkitvault/recall/releases/tag/${tag}`)
|
|
153
|
+
console.error('Tip: set RECALL_RELEASE_TAG=vX.Y.Z if your npm version differs from the CLI release tag.')
|
|
154
|
+
process.exit(1)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const result = spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' })
|
|
159
|
+
if (result.error) {
|
|
160
|
+
console.error(result.error.message || result.error)
|
|
161
|
+
process.exit(1)
|
|
162
|
+
}
|
|
163
|
+
process.exit(result.status === null ? 1 : result.status)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch((e) => {
|
|
167
|
+
console.error(e instanceof Error ? e.message : e)
|
|
168
|
+
process.exit(1)
|
|
169
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@devkitvault/recall",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "recall CLI installer — cloud-synced shell command vault (downloads native binary from GitHub releases)",
|
|
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": "bin/recall.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"bin",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"prepublishOnly": "node --check bin/recall.js"
|
|
45
|
+
},
|
|
46
|
+
"recall": {
|
|
47
|
+
"releaseTag": "v1.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|