teak-cli 1.0.51 → 1.0.53
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 +48 -0
- package/dist/index.js +100 -15
- package/package.json +20 -3
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Teak CLI
|
|
2
|
+
|
|
3
|
+
Command-line client for [Teak](https://teakvault.com), the personal knowledge hub for saving and rediscovering ideas, links, files, notes, and references.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g teak-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
teak --version
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Sign In
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
teak login
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The CLI opens Teak in your browser and stores the resulting session securely in macOS Keychain when available. You can also use an API key:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
teak --api-key teakapi_... cards list
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Common Commands
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
teak add "Design note from today's review" --tags design,research
|
|
31
|
+
teak add --url https://example.com --tags reference
|
|
32
|
+
teak add --file ./screenshot.png --notes "Homepage inspiration"
|
|
33
|
+
teak search "homepage inspiration"
|
|
34
|
+
teak cards list --type link --limit 20
|
|
35
|
+
teak cards get <card-id>
|
|
36
|
+
teak cards update <card-id> --tags design,approved
|
|
37
|
+
teak fav <card-id>
|
|
38
|
+
teak rm <card-id>
|
|
39
|
+
teak tags
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use `--json` on any command for script-friendly output.
|
|
43
|
+
|
|
44
|
+
## Docs
|
|
45
|
+
|
|
46
|
+
- CLI guide: [teakvault.com/docs/cli](https://teakvault.com/docs/cli)
|
|
47
|
+
- API docs: [teakvault.com/docs/api](https://teakvault.com/docs/api)
|
|
48
|
+
- MCP docs: [teakvault.com/docs/mcp](https://teakvault.com/docs/mcp)
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { readFileSync as readFileSync3, realpathSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
|
-
// ../../packages/
|
|
8
|
-
var
|
|
7
|
+
// ../../packages/convex/shared/constants.ts
|
|
8
|
+
var cardTypes = [
|
|
9
9
|
"text",
|
|
10
10
|
"link",
|
|
11
11
|
"image",
|
|
@@ -15,6 +15,61 @@ var CARD_TYPES = [
|
|
|
15
15
|
"palette",
|
|
16
16
|
"quote"
|
|
17
17
|
];
|
|
18
|
+
var MAX_FILE_SIZE = 20 * 1024 * 1024;
|
|
19
|
+
var CARD_TYPES = [...cardTypes];
|
|
20
|
+
var CARD_TYPE_REGISTRY = {
|
|
21
|
+
text: {
|
|
22
|
+
label: "Text",
|
|
23
|
+
icon: "FileText",
|
|
24
|
+
searchLabel: "Text"
|
|
25
|
+
},
|
|
26
|
+
link: {
|
|
27
|
+
label: "Link",
|
|
28
|
+
icon: "Link",
|
|
29
|
+
searchLabel: "Links"
|
|
30
|
+
},
|
|
31
|
+
image: {
|
|
32
|
+
label: "Image",
|
|
33
|
+
icon: "Image",
|
|
34
|
+
searchLabel: "Images"
|
|
35
|
+
},
|
|
36
|
+
video: {
|
|
37
|
+
label: "Video",
|
|
38
|
+
icon: "Video",
|
|
39
|
+
searchLabel: "Videos"
|
|
40
|
+
},
|
|
41
|
+
audio: {
|
|
42
|
+
label: "Audio",
|
|
43
|
+
icon: "Volume2",
|
|
44
|
+
searchLabel: "Audio"
|
|
45
|
+
},
|
|
46
|
+
document: {
|
|
47
|
+
label: "Document",
|
|
48
|
+
icon: "File",
|
|
49
|
+
searchLabel: "Documents"
|
|
50
|
+
},
|
|
51
|
+
palette: {
|
|
52
|
+
label: "Palette",
|
|
53
|
+
icon: "Palette",
|
|
54
|
+
searchLabel: "Palettes"
|
|
55
|
+
},
|
|
56
|
+
quote: {
|
|
57
|
+
label: "Quote",
|
|
58
|
+
icon: "Quote",
|
|
59
|
+
searchLabel: "Quotes"
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var RESERVED_KEYWORDS = [
|
|
63
|
+
...cardTypes.map((cardType) => ({
|
|
64
|
+
value: cardType,
|
|
65
|
+
label: CARD_TYPE_REGISTRY[cardType].searchLabel
|
|
66
|
+
})),
|
|
67
|
+
{ value: "favorites", label: "Favorites" },
|
|
68
|
+
{ value: "trash", label: "Trash" }
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// ../../packages/convex/client/sdk.ts
|
|
72
|
+
var CARD_TYPES2 = cardTypes;
|
|
18
73
|
var ERROR_CODES = [
|
|
19
74
|
"AUTH_REQUIRED",
|
|
20
75
|
"BAD_REQUEST",
|
|
@@ -73,6 +128,21 @@ var normalizeLimit = (limit) => {
|
|
|
73
128
|
}
|
|
74
129
|
return Math.max(1, Math.min(Math.trunc(limit ?? 50), 100));
|
|
75
130
|
};
|
|
131
|
+
var byteLengthForBody = (bytes) => {
|
|
132
|
+
if (bytes instanceof ArrayBuffer) {
|
|
133
|
+
return bytes.byteLength;
|
|
134
|
+
}
|
|
135
|
+
if (ArrayBuffer.isView(bytes)) {
|
|
136
|
+
return bytes.byteLength;
|
|
137
|
+
}
|
|
138
|
+
if (typeof Blob !== "undefined" && bytes instanceof Blob) {
|
|
139
|
+
return bytes.size;
|
|
140
|
+
}
|
|
141
|
+
if (typeof bytes === "string") {
|
|
142
|
+
return new TextEncoder().encode(bytes).byteLength;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
76
146
|
var buildCardsSearchParams = (input) => {
|
|
77
147
|
const search = new URLSearchParams;
|
|
78
148
|
if (input.query?.trim()) {
|
|
@@ -239,9 +309,14 @@ var createTeakClient = (options) => {
|
|
|
239
309
|
uploads: {
|
|
240
310
|
create: (input) => request("/v1/uploads", { body: JSON.stringify(input), method: "POST" }, asUpload),
|
|
241
311
|
putFile: async (uploadUrl, bytes, mimeType) => {
|
|
312
|
+
const headers = new Headers({ "Content-Type": mimeType });
|
|
313
|
+
const byteLength = byteLengthForBody(bytes);
|
|
314
|
+
if (byteLength !== null) {
|
|
315
|
+
headers.set("Content-Length", String(byteLength));
|
|
316
|
+
}
|
|
242
317
|
const response = await fetchImpl(uploadUrl, {
|
|
243
318
|
body: bytes,
|
|
244
|
-
headers
|
|
319
|
+
headers,
|
|
245
320
|
method: "PUT"
|
|
246
321
|
});
|
|
247
322
|
if (!response.ok) {
|
|
@@ -252,7 +327,7 @@ var createTeakClient = (options) => {
|
|
|
252
327
|
};
|
|
253
328
|
};
|
|
254
329
|
var parseTags = (value) => value === undefined ? undefined : Array.from(new Set(value.split(",").map((tag) => tag.trim()).filter(Boolean)));
|
|
255
|
-
var isCardType = (value) =>
|
|
330
|
+
var isCardType = (value) => CARD_TYPES2.includes(value);
|
|
256
331
|
|
|
257
332
|
// ../../node_modules/commander/lib/error.js
|
|
258
333
|
class CommanderError extends Error {
|
|
@@ -2327,6 +2402,7 @@ var VERSION = readPackageVersion();
|
|
|
2327
2402
|
var EXIT = { api: 1, auth: 3, notFound: 4, rateLimited: 5, usage: 2 };
|
|
2328
2403
|
var DEFAULT_API_URL = "https://api.teakvault.com";
|
|
2329
2404
|
var DEFAULT_AUTH_URL = "https://app.teakvault.com";
|
|
2405
|
+
var CLI_OAUTH_SCOPE = "profile email offline_access";
|
|
2330
2406
|
var SERVICE = "com.teakvault.cli";
|
|
2331
2407
|
var ACCOUNT = "default";
|
|
2332
2408
|
var readJson = (value) => {
|
|
@@ -2498,6 +2574,17 @@ var openBrowser = (url) => {
|
|
|
2498
2574
|
const args = platform() === "win32" ? ["/c", "start", "", url] : [url];
|
|
2499
2575
|
spawn(command, args, { detached: true, stdio: "ignore" }).unref();
|
|
2500
2576
|
};
|
|
2577
|
+
var createAuthorizeUrl = (options, params) => {
|
|
2578
|
+
const authUrl = new URL(authorizeEndpoint(options));
|
|
2579
|
+
authUrl.searchParams.set("response_type", "code");
|
|
2580
|
+
authUrl.searchParams.set("client_id", "teak-cli");
|
|
2581
|
+
authUrl.searchParams.set("redirect_uri", params.redirectUri);
|
|
2582
|
+
authUrl.searchParams.set("code_challenge", params.codeChallenge);
|
|
2583
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
2584
|
+
authUrl.searchParams.set("scope", CLI_OAUTH_SCOPE);
|
|
2585
|
+
authUrl.searchParams.set("state", params.state);
|
|
2586
|
+
return authUrl;
|
|
2587
|
+
};
|
|
2501
2588
|
var login = async (options) => {
|
|
2502
2589
|
const verifier = b64url(randomBytes(32));
|
|
2503
2590
|
const state = b64url(randomBytes(24));
|
|
@@ -2536,13 +2623,11 @@ var login = async (options) => {
|
|
|
2536
2623
|
}
|
|
2537
2624
|
});
|
|
2538
2625
|
server.listen(port, "127.0.0.1", () => {
|
|
2539
|
-
const authUrl =
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
2545
|
-
authUrl.searchParams.set("state", state);
|
|
2626
|
+
const authUrl = createAuthorizeUrl(options, {
|
|
2627
|
+
codeChallenge: sha256(verifier),
|
|
2628
|
+
redirectUri,
|
|
2629
|
+
state
|
|
2630
|
+
});
|
|
2546
2631
|
process.stdout.write(`${authUrl.toString()}
|
|
2547
2632
|
`);
|
|
2548
2633
|
if (options.browser !== false) {
|
|
@@ -2565,7 +2650,7 @@ var login = async (options) => {
|
|
|
2565
2650
|
|
|
2566
2651
|
// src/files.ts
|
|
2567
2652
|
var MAX_STDIN_BYTES = 1024 * 1024;
|
|
2568
|
-
var
|
|
2653
|
+
var MAX_FILE_SIZE2 = 20 * 1024 * 1024;
|
|
2569
2654
|
var readStdin = async () => {
|
|
2570
2655
|
if (process.stdin.isTTY) {
|
|
2571
2656
|
return "";
|
|
@@ -2622,8 +2707,8 @@ var addCard = async (input, options) => {
|
|
|
2622
2707
|
const { candidate, raw } = await resolveAddInput(input, options.file);
|
|
2623
2708
|
if (candidate && existsSync2(candidate) && statSync(candidate).isFile()) {
|
|
2624
2709
|
const stats = statSync(candidate);
|
|
2625
|
-
if (stats.size >
|
|
2626
|
-
throw new InvalidArgumentError(`file must be at most ${
|
|
2710
|
+
if (stats.size > MAX_FILE_SIZE2) {
|
|
2711
|
+
throw new InvalidArgumentError(`file must be at most ${MAX_FILE_SIZE2} bytes`);
|
|
2627
2712
|
}
|
|
2628
2713
|
const mimeType = mimeFor(candidate);
|
|
2629
2714
|
const upload = await api.uploads.create({
|
|
@@ -2706,7 +2791,7 @@ var parseSince = (value) => {
|
|
|
2706
2791
|
};
|
|
2707
2792
|
var parseType = (value) => {
|
|
2708
2793
|
if (!isCardType(value)) {
|
|
2709
|
-
throw new InvalidArgumentError(`type must be one of: ${
|
|
2794
|
+
throw new InvalidArgumentError(`type must be one of: ${CARD_TYPES2.join(", ")}`);
|
|
2710
2795
|
}
|
|
2711
2796
|
return value;
|
|
2712
2797
|
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teak-cli",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Command
|
|
3
|
+
"version": "1.0.53",
|
|
4
|
+
"description": "Command-line client for Teak, the personal knowledge hub for saving and rediscovering ideas.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"teak": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"teak",
|
|
15
|
+
"cli",
|
|
16
|
+
"knowledge-base",
|
|
17
|
+
"bookmark-manager",
|
|
18
|
+
"notes",
|
|
19
|
+
"mcp",
|
|
20
|
+
"api-client",
|
|
21
|
+
"productivity"
|
|
11
22
|
],
|
|
12
23
|
"engines": {
|
|
13
24
|
"node": ">=20"
|
|
@@ -21,6 +32,7 @@
|
|
|
21
32
|
"commander": "^15.0.0"
|
|
22
33
|
},
|
|
23
34
|
"devDependencies": {
|
|
35
|
+
"@teak/convex": "workspace:*",
|
|
24
36
|
"@types/node": "^25.9.3",
|
|
25
37
|
"typescript": "^6.0.3"
|
|
26
38
|
},
|
|
@@ -33,5 +45,10 @@
|
|
|
33
45
|
"url": "git+https://github.com/praveenjuge/teak.git",
|
|
34
46
|
"directory": "apps/cli"
|
|
35
47
|
},
|
|
48
|
+
"homepage": "https://teakvault.com/docs/cli",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/praveenjuge/teak/issues"
|
|
51
|
+
},
|
|
52
|
+
"author": "Praveen Juge <hello@praveenjuge.com>",
|
|
36
53
|
"license": "MIT"
|
|
37
54
|
}
|