ticktick-cli 1.0.1 → 1.0.3
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 +7 -11
- package/dist/auth.js +1 -1
- package/dist/cli.js +34 -3
- package/package.json +3 -7
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A TypeScript CLI wrapper for the TickTick Open API documented at:
|
|
|
5
5
|
- https://developer.ticktick.com/
|
|
6
6
|
- https://developer.ticktick.com/docs#/openapi
|
|
7
7
|
|
|
8
|
-
`v1.0.
|
|
8
|
+
`v1.0.2` covers the documented OAuth flow plus every documented task and project endpoint.
|
|
9
9
|
|
|
10
10
|
The CLI is available as both `ticktick` and the short alias `tt`.
|
|
11
11
|
|
|
@@ -100,6 +100,8 @@ Interactive login:
|
|
|
100
100
|
ticktick auth login
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
Successful auth stores the token in your local config file and masks secrets in the terminal output by default.
|
|
104
|
+
|
|
103
105
|
If you already have an authorization code:
|
|
104
106
|
|
|
105
107
|
```bash
|
|
@@ -118,6 +120,8 @@ Check current auth state:
|
|
|
118
120
|
ticktick auth status
|
|
119
121
|
```
|
|
120
122
|
|
|
123
|
+
If you need the raw token values in the terminal, add `--show-secrets` to `auth login`, `auth exchange`, or `auth status`.
|
|
124
|
+
|
|
121
125
|
Clear the stored access token:
|
|
122
126
|
|
|
123
127
|
```bash
|
|
@@ -205,20 +209,12 @@ ticktick request POST https://httpbin.org/post --no-auth --json '{"hello":"world
|
|
|
205
209
|
|
|
206
210
|
## Development
|
|
207
211
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
```bash
|
|
211
|
-
npm test
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Run the enforced coverage check:
|
|
212
|
+
Build the CLI:
|
|
215
213
|
|
|
216
214
|
```bash
|
|
217
|
-
npm run
|
|
215
|
+
npm run build
|
|
218
216
|
```
|
|
219
217
|
|
|
220
|
-
`npm run coverage` currently enforces `100%` line, branch, and function coverage on the published runtime files.
|
|
221
|
-
|
|
222
218
|
## Notes
|
|
223
219
|
|
|
224
220
|
- The docs currently show `api.ticktick.com` for most endpoints, but `api.dida365.com` in the examples for `task/move`, `task/completed`, and `task/filter`. This CLI defaults to the selected service profile and lets you override base URLs explicitly if your account needs something different.
|
package/dist/auth.js
CHANGED
|
@@ -118,7 +118,7 @@ export async function openBrowserWith(url, options = {}) {
|
|
|
118
118
|
const stderr = options.stderr ?? ((text) => process.stderr.write(text));
|
|
119
119
|
try {
|
|
120
120
|
if (platform === "win32") {
|
|
121
|
-
await run("
|
|
121
|
+
await run("rundll32", ["url.dll,FileProtocolHandler", url]);
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
124
|
if (platform === "darwin") {
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
2
3
|
import { buildAuthorizationUrl, exchangeAuthorizationCode, isLoopbackRedirect, openBrowser, waitForOAuthCode, } from "./auth.js";
|
|
3
4
|
import { TickTickClient } from "./client.js";
|
|
4
5
|
import { loadStoredConfig, resolveRuntimeConfig, saveStoredConfig, validateService, } from "./config.js";
|
|
@@ -82,6 +83,7 @@ function buildAuthCommands(root, dependencies) {
|
|
|
82
83
|
.command("login")
|
|
83
84
|
.description("Open the OAuth flow, exchange the code, and store the access token")
|
|
84
85
|
.option("--timeout-ms <number>", "Timeout while waiting for the callback", parseInteger, 120000)
|
|
86
|
+
.option("--show-secrets", "Include access token and refresh token in the output")
|
|
85
87
|
.action(async (...args) => {
|
|
86
88
|
const command = args.at(-1);
|
|
87
89
|
const options = command.optsWithGlobals();
|
|
@@ -96,24 +98,27 @@ function buildAuthCommands(root, dependencies) {
|
|
|
96
98
|
});
|
|
97
99
|
return;
|
|
98
100
|
}
|
|
101
|
+
dependencies.stderr(`Waiting for OAuth callback on ${config.redirectUri}...\n`);
|
|
99
102
|
const codePromise = dependencies.waitForOAuthCode(config.redirectUri, state, options.timeoutMs);
|
|
100
103
|
await dependencies.openBrowser(url);
|
|
101
104
|
const code = await codePromise;
|
|
102
105
|
const token = await dependencies.exchangeAuthorizationCode(config, code);
|
|
103
106
|
await persistConfig(config, token, dependencies);
|
|
104
|
-
dependencies.printJson(token);
|
|
107
|
+
dependencies.printJson(formatAuthSuccess(config, token, options.showSecrets, dependencies));
|
|
105
108
|
});
|
|
106
109
|
auth
|
|
107
110
|
.command("exchange <code>")
|
|
108
111
|
.description("Exchange an authorization code for an access token")
|
|
112
|
+
.option("--show-secrets", "Include access token and refresh token in the output")
|
|
109
113
|
.action(async (...args) => {
|
|
110
114
|
const command = args.at(-1);
|
|
111
115
|
const [code] = args;
|
|
112
|
-
const
|
|
116
|
+
const options = command.optsWithGlobals();
|
|
117
|
+
const config = await dependencies.resolveRuntimeConfig(runtimeOverrides(options, dependencies));
|
|
113
118
|
requireClientCredentials(config);
|
|
114
119
|
const token = await dependencies.exchangeAuthorizationCode(config, code);
|
|
115
120
|
await persistConfig(config, token, dependencies);
|
|
116
|
-
dependencies.printJson(token);
|
|
121
|
+
dependencies.printJson(formatAuthSuccess(config, token, options.showSecrets, dependencies));
|
|
117
122
|
});
|
|
118
123
|
auth
|
|
119
124
|
.command("status")
|
|
@@ -508,6 +513,26 @@ async function persistConfig(runtime, token, dependencies) {
|
|
|
508
513
|
};
|
|
509
514
|
await dependencies.saveStoredConfig(runtime.configFile, next);
|
|
510
515
|
}
|
|
516
|
+
function formatAuthSuccess(runtime, token, showSecrets, dependencies) {
|
|
517
|
+
return {
|
|
518
|
+
ok: true,
|
|
519
|
+
message: "Authorization complete.",
|
|
520
|
+
service: runtime.service,
|
|
521
|
+
configFile: runtime.configFile,
|
|
522
|
+
redirectUri: runtime.redirectUri,
|
|
523
|
+
scope: token.scope ?? runtime.scopes,
|
|
524
|
+
tokenType: token.token_type,
|
|
525
|
+
expiresIn: token.expires_in,
|
|
526
|
+
accessToken: showSecrets
|
|
527
|
+
? token.access_token
|
|
528
|
+
: dependencies.maskSecret(token.access_token),
|
|
529
|
+
refreshToken: token.refresh_token
|
|
530
|
+
? showSecrets
|
|
531
|
+
? token.refresh_token
|
|
532
|
+
: dependencies.maskSecret(token.refresh_token)
|
|
533
|
+
: undefined,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
511
536
|
function requireClientCredentials(config) {
|
|
512
537
|
if (!config.clientId || !config.clientSecret) {
|
|
513
538
|
throw new Error("Client credentials are required. Set TICKTICK_CLIENT_ID and TICKTICK_CLIENT_SECRET or use `ticktick config set`.");
|
|
@@ -524,3 +549,9 @@ function collectString(value, previous = []) {
|
|
|
524
549
|
function collectInteger(value, previous = []) {
|
|
525
550
|
return [...previous, parseInteger(value)];
|
|
526
551
|
}
|
|
552
|
+
function isDirectExecution() {
|
|
553
|
+
return Boolean(process.argv[1]) && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
554
|
+
}
|
|
555
|
+
if (isDirectExecution()) {
|
|
556
|
+
await main();
|
|
557
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ticktick-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "CLI wrapper for the TickTick Open API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -14,14 +14,10 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "npm run clean && tsc -p tsconfig.json",
|
|
17
|
-
"
|
|
18
|
-
"check": "npm run coverage",
|
|
17
|
+
"check": "npm run build",
|
|
19
18
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
20
|
-
"clean:coverage": "node -e \"require('node:fs').rmSync('.coverage-dist',{recursive:true,force:true})\"",
|
|
21
19
|
"dev": "tsx src/cli.ts",
|
|
22
|
-
"prepack": "npm run
|
|
23
|
-
"test": "npm run build && node --import tsx --test src/test/*.test.ts",
|
|
24
|
-
"coverage": "npm run build:coverage && node --experimental-test-coverage --test-coverage-lines=100 --test-coverage-functions=100 --test-coverage-branches=100 --test-coverage-include=.coverage-dist/*.js --test-coverage-exclude=.coverage-dist/bin.js --test .coverage-dist/test/*.test.js"
|
|
20
|
+
"prepack": "npm run build"
|
|
25
21
|
},
|
|
26
22
|
"keywords": [
|
|
27
23
|
"ticktick",
|