@lpm-registry/cli 0.2.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/CHANGELOG.md +36 -0
- package/LICENSE +15 -0
- package/README.md +406 -0
- package/bin/lpm.js +334 -0
- package/index.d.ts +131 -0
- package/index.js +31 -0
- package/lib/api.js +324 -0
- package/lib/commands/add.js +1217 -0
- package/lib/commands/audit.js +283 -0
- package/lib/commands/cache.js +209 -0
- package/lib/commands/check-name.js +112 -0
- package/lib/commands/config.js +174 -0
- package/lib/commands/doctor.js +142 -0
- package/lib/commands/info.js +215 -0
- package/lib/commands/init.js +146 -0
- package/lib/commands/install.js +217 -0
- package/lib/commands/login.js +547 -0
- package/lib/commands/logout.js +94 -0
- package/lib/commands/marketplace-compare.js +164 -0
- package/lib/commands/marketplace-earnings.js +89 -0
- package/lib/commands/mcp-setup.js +363 -0
- package/lib/commands/open.js +82 -0
- package/lib/commands/outdated.js +291 -0
- package/lib/commands/pool-stats.js +100 -0
- package/lib/commands/publish.js +707 -0
- package/lib/commands/quality.js +211 -0
- package/lib/commands/remove.js +82 -0
- package/lib/commands/run.js +14 -0
- package/lib/commands/search.js +143 -0
- package/lib/commands/setup.js +92 -0
- package/lib/commands/skills.js +863 -0
- package/lib/commands/token-rotate.js +25 -0
- package/lib/commands/whoami.js +129 -0
- package/lib/config.js +240 -0
- package/lib/constants.js +190 -0
- package/lib/ecosystem.js +501 -0
- package/lib/editors.js +215 -0
- package/lib/import-rewriter.js +364 -0
- package/lib/install-targets/mcp-server.js +245 -0
- package/lib/install-targets/vscode-extension.js +178 -0
- package/lib/install-targets.js +82 -0
- package/lib/integrity.js +179 -0
- package/lib/lpm-config-prompts.js +102 -0
- package/lib/lpm-config.js +408 -0
- package/lib/project-utils.js +152 -0
- package/lib/quality/checks.js +654 -0
- package/lib/quality/display.js +139 -0
- package/lib/quality/score.js +115 -0
- package/lib/quality/swift-checks.js +447 -0
- package/lib/safe-path.js +180 -0
- package/lib/secure-store.js +288 -0
- package/lib/swift-project.js +637 -0
- package/lib/ui.js +40 -0
- package/package.json +74 -0
package/lib/safe-path.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe Path Resolution
|
|
3
|
+
*
|
|
4
|
+
* Provides path traversal protection for file operations.
|
|
5
|
+
* Ensures all paths stay within a designated base directory.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/lib/safe-path
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { isAbsolute, normalize, relative, resolve } from "node:path"
|
|
11
|
+
import { ERROR_MESSAGES } from "./constants.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a path safely within a base directory.
|
|
15
|
+
* Prevents path traversal attacks using ../ sequences or absolute paths.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} basePath - The base directory (must be absolute)
|
|
18
|
+
* @param {string} userPath - The user-provided path to resolve
|
|
19
|
+
* @returns {{ safe: boolean, resolvedPath?: string, error?: string }}
|
|
20
|
+
*/
|
|
21
|
+
export function resolveSafePath(basePath, userPath) {
|
|
22
|
+
// Base path must be absolute
|
|
23
|
+
if (!isAbsolute(basePath)) {
|
|
24
|
+
return {
|
|
25
|
+
safe: false,
|
|
26
|
+
error: "Base path must be absolute",
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Reject absolute paths from user input
|
|
31
|
+
if (isAbsolute(userPath)) {
|
|
32
|
+
return {
|
|
33
|
+
safe: false,
|
|
34
|
+
error: ERROR_MESSAGES.pathTraversal,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Normalize the base path
|
|
39
|
+
const normalizedBase = normalize(basePath)
|
|
40
|
+
|
|
41
|
+
// Resolve the full path
|
|
42
|
+
const resolvedPath = resolve(normalizedBase, userPath)
|
|
43
|
+
|
|
44
|
+
// Normalize the resolved path
|
|
45
|
+
const normalizedResolved = normalize(resolvedPath)
|
|
46
|
+
|
|
47
|
+
// Check if resolved path is within base
|
|
48
|
+
const relativePath = relative(normalizedBase, normalizedResolved)
|
|
49
|
+
|
|
50
|
+
// Path is outside base if:
|
|
51
|
+
// 1. It starts with '..' (goes above base)
|
|
52
|
+
// 2. It's an absolute path (on Windows, this can happen with drive changes)
|
|
53
|
+
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
54
|
+
return {
|
|
55
|
+
safe: false,
|
|
56
|
+
error: ERROR_MESSAGES.pathTraversal,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
safe: true,
|
|
62
|
+
resolvedPath: normalizedResolved,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if a path contains dangerous patterns.
|
|
68
|
+
* This is a quick check before more expensive resolution.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} userPath - The path to check
|
|
71
|
+
* @returns {boolean} - True if the path looks dangerous
|
|
72
|
+
*/
|
|
73
|
+
export function hasDangerousPatterns(userPath) {
|
|
74
|
+
if (!userPath || typeof userPath !== "string") {
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Dangerous patterns
|
|
79
|
+
const dangerousPatterns = [
|
|
80
|
+
// Null bytes (can bypass checks in some systems)
|
|
81
|
+
"\0",
|
|
82
|
+
// Windows drive letters
|
|
83
|
+
/^[a-zA-Z]:/,
|
|
84
|
+
// UNC paths
|
|
85
|
+
/^\\\\|^\/\//,
|
|
86
|
+
// Excessive parent traversal
|
|
87
|
+
/\.\.[/\\]\.\.[/\\]\.\.[/\\]/,
|
|
88
|
+
// Hidden files/directories (optional, depends on use case)
|
|
89
|
+
// /\/\./,
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
for (const pattern of dangerousPatterns) {
|
|
93
|
+
if (typeof pattern === "string") {
|
|
94
|
+
if (userPath.includes(pattern)) return true
|
|
95
|
+
} else {
|
|
96
|
+
if (pattern.test(userPath)) return true
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return false
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Sanitize a filename by removing or replacing dangerous characters.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} filename - The filename to sanitize
|
|
107
|
+
* @returns {string} - Sanitized filename
|
|
108
|
+
*/
|
|
109
|
+
export function sanitizeFilename(filename) {
|
|
110
|
+
if (!filename || typeof filename !== "string") {
|
|
111
|
+
return ""
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
filename
|
|
116
|
+
// Remove null bytes
|
|
117
|
+
.replace(/\0/g, "")
|
|
118
|
+
// Replace path separators
|
|
119
|
+
.replace(/[/\\]/g, "-")
|
|
120
|
+
// Remove other dangerous characters
|
|
121
|
+
.replace(/[<>:"|?*]/g, "")
|
|
122
|
+
// Collapse multiple dashes
|
|
123
|
+
.replace(/-+/g, "-")
|
|
124
|
+
// Trim dashes from ends
|
|
125
|
+
.replace(/^-+|-+$/g, "")
|
|
126
|
+
// Limit length
|
|
127
|
+
.slice(0, 255)
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Validate a package component path.
|
|
133
|
+
* Used by the `lpm add` command to ensure extracted files stay in bounds.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} projectRoot - The project root directory
|
|
136
|
+
* @param {string} componentPath - The path where the component should be extracted
|
|
137
|
+
* @returns {{ valid: boolean, resolvedPath?: string, error?: string }}
|
|
138
|
+
*/
|
|
139
|
+
export function validateComponentPath(projectRoot, componentPath) {
|
|
140
|
+
// Quick dangerous pattern check
|
|
141
|
+
if (hasDangerousPatterns(componentPath)) {
|
|
142
|
+
return {
|
|
143
|
+
valid: false,
|
|
144
|
+
error: ERROR_MESSAGES.pathTraversal,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Full path resolution and validation
|
|
149
|
+
const result = resolveSafePath(projectRoot, componentPath)
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
valid: result.safe,
|
|
153
|
+
resolvedPath: result.resolvedPath,
|
|
154
|
+
error: result.error,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Validate each file path in a tarball before extraction.
|
|
160
|
+
* Prevents zip slip attacks.
|
|
161
|
+
*
|
|
162
|
+
* @param {string} extractDir - The extraction directory
|
|
163
|
+
* @param {string[]} filePaths - Array of file paths from the tarball
|
|
164
|
+
* @returns {{ valid: boolean, invalidPaths: string[] }}
|
|
165
|
+
*/
|
|
166
|
+
export function validateTarballPaths(extractDir, filePaths) {
|
|
167
|
+
const invalidPaths = []
|
|
168
|
+
|
|
169
|
+
for (const filePath of filePaths) {
|
|
170
|
+
const result = resolveSafePath(extractDir, filePath)
|
|
171
|
+
if (!result.safe) {
|
|
172
|
+
invalidPaths.push(filePath)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
valid: invalidPaths.length === 0,
|
|
178
|
+
invalidPaths,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure Store - OS Keychain Storage with Fallback
|
|
3
|
+
*
|
|
4
|
+
* Provides secure credential storage using the system keychain.
|
|
5
|
+
* Falls back to encrypted file storage if keytar is unavailable.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/lib/secure-store
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
createCipheriv,
|
|
12
|
+
createDecipheriv,
|
|
13
|
+
randomBytes,
|
|
14
|
+
scryptSync,
|
|
15
|
+
} from "node:crypto"
|
|
16
|
+
import {
|
|
17
|
+
existsSync,
|
|
18
|
+
mkdirSync,
|
|
19
|
+
readFileSync,
|
|
20
|
+
unlinkSync,
|
|
21
|
+
writeFileSync,
|
|
22
|
+
} from "node:fs"
|
|
23
|
+
import { homedir } from "node:os"
|
|
24
|
+
import { join } from "node:path"
|
|
25
|
+
import { KEYTAR_ACCOUNT_NAME, KEYTAR_SERVICE_NAME } from "./constants.js"
|
|
26
|
+
|
|
27
|
+
/** @type {import('keytar') | null} */
|
|
28
|
+
let keytar = null
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Try to load keytar for native keychain access.
|
|
32
|
+
* Falls back gracefully if not available.
|
|
33
|
+
*/
|
|
34
|
+
async function loadKeytar() {
|
|
35
|
+
if (keytar !== null) return keytar
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// Dynamic import to avoid hard dependency
|
|
39
|
+
const mod = await import("keytar")
|
|
40
|
+
// ESM dynamic import returns { default: keytarModule }
|
|
41
|
+
keytar = mod.default || mod
|
|
42
|
+
return keytar
|
|
43
|
+
} catch {
|
|
44
|
+
// keytar not available (missing native dependencies)
|
|
45
|
+
keytar = false
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Fallback Encrypted File Storage
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
const ENCRYPTED_STORE_DIR = join(homedir(), ".lpm")
|
|
55
|
+
const ENCRYPTED_STORE_FILE = join(ENCRYPTED_STORE_DIR, ".credentials")
|
|
56
|
+
const SALT_FILE = join(ENCRYPTED_STORE_DIR, ".salt")
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get or create encryption salt.
|
|
60
|
+
* @returns {Buffer}
|
|
61
|
+
*/
|
|
62
|
+
function getOrCreateSalt() {
|
|
63
|
+
if (existsSync(SALT_FILE)) {
|
|
64
|
+
return readFileSync(SALT_FILE)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const salt = randomBytes(32)
|
|
68
|
+
if (!existsSync(ENCRYPTED_STORE_DIR)) {
|
|
69
|
+
mkdirSync(ENCRYPTED_STORE_DIR, { recursive: true, mode: 0o700 })
|
|
70
|
+
}
|
|
71
|
+
writeFileSync(SALT_FILE, salt, { mode: 0o600 })
|
|
72
|
+
return salt
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Derive encryption key from machine-specific data.
|
|
77
|
+
* Uses a combination of hostname, username, and random salt.
|
|
78
|
+
* @returns {Buffer}
|
|
79
|
+
*/
|
|
80
|
+
function deriveKey() {
|
|
81
|
+
const salt = getOrCreateSalt()
|
|
82
|
+
// Use machine-specific data as part of the key derivation
|
|
83
|
+
const machineId = `${homedir()}-${process.env.USER || "user"}`
|
|
84
|
+
return scryptSync(machineId, salt, 32)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Encrypt a value using AES-256-GCM.
|
|
89
|
+
* @param {string} value - The value to encrypt
|
|
90
|
+
* @returns {string} - Base64 encoded encrypted data
|
|
91
|
+
*/
|
|
92
|
+
function encrypt(value) {
|
|
93
|
+
const key = deriveKey()
|
|
94
|
+
const iv = randomBytes(16)
|
|
95
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv)
|
|
96
|
+
|
|
97
|
+
let encrypted = cipher.update(value, "utf8", "base64")
|
|
98
|
+
encrypted += cipher.final("base64")
|
|
99
|
+
|
|
100
|
+
const authTag = cipher.getAuthTag()
|
|
101
|
+
|
|
102
|
+
// Format: iv:authTag:encrypted
|
|
103
|
+
return `${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted}`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Decrypt a value using AES-256-GCM.
|
|
108
|
+
* @param {string} encryptedValue - Base64 encoded encrypted data
|
|
109
|
+
* @returns {string | null} - Decrypted value or null if failed
|
|
110
|
+
*/
|
|
111
|
+
function decrypt(encryptedValue) {
|
|
112
|
+
try {
|
|
113
|
+
const key = deriveKey()
|
|
114
|
+
const [ivBase64, authTagBase64, encrypted] = encryptedValue.split(":")
|
|
115
|
+
|
|
116
|
+
const iv = Buffer.from(ivBase64, "base64")
|
|
117
|
+
const authTag = Buffer.from(authTagBase64, "base64")
|
|
118
|
+
|
|
119
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv)
|
|
120
|
+
decipher.setAuthTag(authTag)
|
|
121
|
+
|
|
122
|
+
let decrypted = decipher.update(encrypted, "base64", "utf8")
|
|
123
|
+
decrypted += decipher.final("utf8")
|
|
124
|
+
|
|
125
|
+
return decrypted
|
|
126
|
+
} catch {
|
|
127
|
+
// Decryption failed (wrong key, corrupted data, etc.)
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Read encrypted store from file.
|
|
134
|
+
* @returns {Record<string, string>}
|
|
135
|
+
*/
|
|
136
|
+
function readEncryptedStore() {
|
|
137
|
+
if (!existsSync(ENCRYPTED_STORE_FILE)) {
|
|
138
|
+
return {}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const content = readFileSync(ENCRYPTED_STORE_FILE, "utf8")
|
|
143
|
+
const decrypted = decrypt(content)
|
|
144
|
+
if (!decrypted) return {}
|
|
145
|
+
return JSON.parse(decrypted)
|
|
146
|
+
} catch {
|
|
147
|
+
return {}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Write encrypted store to file.
|
|
153
|
+
* @param {Record<string, string>} store
|
|
154
|
+
*/
|
|
155
|
+
function writeEncryptedStore(store) {
|
|
156
|
+
if (!existsSync(ENCRYPTED_STORE_DIR)) {
|
|
157
|
+
mkdirSync(ENCRYPTED_STORE_DIR, { recursive: true, mode: 0o700 })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const content = encrypt(JSON.stringify(store))
|
|
161
|
+
writeFileSync(ENCRYPTED_STORE_FILE, content, { mode: 0o600 })
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// Public API
|
|
166
|
+
// ============================================================================
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Store a secret securely.
|
|
170
|
+
* Uses OS keychain if available, falls back to encrypted file.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} key - The key to store the secret under
|
|
173
|
+
* @param {string} value - The secret value
|
|
174
|
+
* @returns {Promise<void>}
|
|
175
|
+
*/
|
|
176
|
+
export async function setSecret(key, value) {
|
|
177
|
+
const kt = await loadKeytar()
|
|
178
|
+
|
|
179
|
+
if (kt) {
|
|
180
|
+
await kt.setPassword(KEYTAR_SERVICE_NAME, key, value)
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Fallback to encrypted file
|
|
185
|
+
const store = readEncryptedStore()
|
|
186
|
+
store[key] = value
|
|
187
|
+
writeEncryptedStore(store)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Retrieve a secret.
|
|
192
|
+
* Uses OS keychain if available, falls back to encrypted file.
|
|
193
|
+
*
|
|
194
|
+
* @param {string} key - The key to retrieve
|
|
195
|
+
* @returns {Promise<string | null>}
|
|
196
|
+
*/
|
|
197
|
+
export async function getSecret(key) {
|
|
198
|
+
const kt = await loadKeytar()
|
|
199
|
+
|
|
200
|
+
if (kt) {
|
|
201
|
+
return kt.getPassword(KEYTAR_SERVICE_NAME, key)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Fallback to encrypted file
|
|
205
|
+
const store = readEncryptedStore()
|
|
206
|
+
return store[key] || null
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Delete a secret.
|
|
211
|
+
* Uses OS keychain if available, falls back to encrypted file.
|
|
212
|
+
*
|
|
213
|
+
* @param {string} key - The key to delete
|
|
214
|
+
* @returns {Promise<boolean>}
|
|
215
|
+
*/
|
|
216
|
+
export async function deleteSecret(key) {
|
|
217
|
+
const kt = await loadKeytar()
|
|
218
|
+
|
|
219
|
+
if (kt) {
|
|
220
|
+
return kt.deletePassword(KEYTAR_SERVICE_NAME, key)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Fallback to encrypted file
|
|
224
|
+
const store = readEncryptedStore()
|
|
225
|
+
if (key in store) {
|
|
226
|
+
delete store[key]
|
|
227
|
+
writeEncryptedStore(store)
|
|
228
|
+
return true
|
|
229
|
+
}
|
|
230
|
+
return false
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Clear all stored secrets.
|
|
235
|
+
* @returns {Promise<void>}
|
|
236
|
+
*/
|
|
237
|
+
export async function clearAllSecrets() {
|
|
238
|
+
const kt = await loadKeytar()
|
|
239
|
+
|
|
240
|
+
if (kt) {
|
|
241
|
+
// keytar doesn't have a "clear all" - delete known keys
|
|
242
|
+
await kt.deletePassword(KEYTAR_SERVICE_NAME, KEYTAR_ACCOUNT_NAME)
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Fallback: delete the encrypted store file
|
|
247
|
+
if (existsSync(ENCRYPTED_STORE_FILE)) {
|
|
248
|
+
unlinkSync(ENCRYPTED_STORE_FILE)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Check if secure storage is using native keychain.
|
|
254
|
+
* @returns {Promise<boolean>}
|
|
255
|
+
*/
|
|
256
|
+
export async function isUsingKeychain() {
|
|
257
|
+
const kt = await loadKeytar()
|
|
258
|
+
return kt !== null && kt !== false
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Token-Specific Helpers
|
|
263
|
+
// ============================================================================
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get the stored auth token.
|
|
267
|
+
* @returns {Promise<string | null>}
|
|
268
|
+
*/
|
|
269
|
+
export async function getToken() {
|
|
270
|
+
return getSecret(KEYTAR_ACCOUNT_NAME)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Set the auth token.
|
|
275
|
+
* @param {string} token
|
|
276
|
+
* @returns {Promise<void>}
|
|
277
|
+
*/
|
|
278
|
+
export async function setToken(token) {
|
|
279
|
+
return setSecret(KEYTAR_ACCOUNT_NAME, token)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Clear the auth token.
|
|
284
|
+
* @returns {Promise<boolean>}
|
|
285
|
+
*/
|
|
286
|
+
export async function clearToken() {
|
|
287
|
+
return deleteSecret(KEYTAR_ACCOUNT_NAME)
|
|
288
|
+
}
|