@delicious233/dida-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/bin/dida ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { spawnSync } = require("child_process");
6
+
7
+ const exe = process.platform === "win32" ? "dida.exe" : "dida-bin";
8
+ const binary = path.join(__dirname, exe);
9
+
10
+ if (!fs.existsSync(binary)) {
11
+ console.error("DidaCLI binary is missing. Try reinstalling the package.");
12
+ process.exit(1);
13
+ }
14
+
15
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
16
+ if (result.error) {
17
+ console.error(result.error.message);
18
+ process.exit(1);
19
+ }
20
+ process.exit(result.status ?? 0);
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@delicious233/dida-cli",
3
+ "version": "0.2.0",
4
+ "private": false,
5
+ "description": "Agent-friendly CLI for Dida365 / TickTick — task automation with stable JSON output.",
6
+ "bin": {
7
+ "dida": "bin/dida"
8
+ },
9
+ "scripts": {
10
+ "postinstall": "node scripts/install.js"
11
+ },
12
+ "files": [
13
+ "bin/dida",
14
+ "scripts/install.js"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/DeliciousBuding/dida-cli.git",
19
+ "directory": "npm"
20
+ },
21
+ "homepage": "https://deliciousbuding.github.io/dida-cli/",
22
+ "bugs": {
23
+ "url": "https://github.com/DeliciousBuding/dida-cli/issues"
24
+ },
25
+ "keywords": [
26
+ "dida365",
27
+ "ticktick",
28
+ "cli",
29
+ "task-management",
30
+ "automation",
31
+ "agent",
32
+ "json",
33
+ "productivity"
34
+ ],
35
+ "author": "DeliciousBuding",
36
+ "license": "MIT",
37
+ "engines": {
38
+ "node": ">=18"
39
+ }
40
+ }
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+ const crypto = require("crypto");
7
+ const https = require("https");
8
+ const { execFileSync } = require("child_process");
9
+
10
+ const repo = process.env.DIDA_REPO || "DeliciousBuding/dida-cli";
11
+ const version = process.env.DIDA_VERSION || "";
12
+ const binDir = path.join(__dirname, "..", "bin");
13
+
14
+ function platformName() {
15
+ if (process.platform === "win32") return "windows";
16
+ if (process.platform === "linux") return "linux";
17
+ if (process.platform === "darwin") return "darwin";
18
+ throw new Error(`unsupported platform: ${process.platform}`);
19
+ }
20
+
21
+ function archName() {
22
+ if (process.arch === "x64") return "amd64";
23
+ if (process.arch === "arm64") return "arm64";
24
+ throw new Error(`unsupported architecture: ${process.arch}`);
25
+ }
26
+
27
+ function requestBuffer(url) {
28
+ return new Promise((resolve, reject) => {
29
+ https.get(url, { headers: { "User-Agent": "dida-cli-npm-installer" } }, (res) => {
30
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
31
+ requestBuffer(res.headers.location).then(resolve, reject);
32
+ return;
33
+ }
34
+ if (res.statusCode !== 200) {
35
+ reject(new Error(`download failed ${res.statusCode}: ${url}`));
36
+ return;
37
+ }
38
+ const chunks = [];
39
+ res.on("data", (chunk) => chunks.push(chunk));
40
+ res.on("end", () => resolve(Buffer.concat(chunks)));
41
+ }).on("error", reject);
42
+ });
43
+ }
44
+
45
+ function sha256(buffer) {
46
+ return crypto.createHash("sha256").update(buffer).digest("hex");
47
+ }
48
+
49
+ async function main() {
50
+ const osName = platformName();
51
+ const arch = archName();
52
+ const ext = osName === "windows" ? "zip" : "tar.gz";
53
+ const exe = osName === "windows" ? "dida.exe" : "dida";
54
+ const installedExe = osName === "windows" ? "dida.exe" : "dida-bin";
55
+ let resolvedVersion = version;
56
+ let base = version
57
+ ? `https://github.com/${repo}/releases/download/${version}`
58
+ : `https://github.com/${repo}/releases/latest/download`;
59
+ const checksums = await requestBuffer(`${base}/checksums.txt`);
60
+ let asset = "";
61
+
62
+ if (resolvedVersion) {
63
+ asset = `dida_${resolvedVersion}_${osName}_${arch}.${ext}`;
64
+ } else {
65
+ const suffix = `_${osName}_${arch}.${ext}`;
66
+ asset = checksums.toString("utf8").split(/\r?\n/)
67
+ .map((line) => line.trim().split(/\s+/)[1])
68
+ .find((name) => name && name.startsWith("dida_v") && name.endsWith(suffix));
69
+ if (asset) {
70
+ const match = asset.match(new RegExp(`^dida_(v[^_]+)_${osName}_${arch}\\.${ext.replace(".", "\\.")}$`));
71
+ if (match) resolvedVersion = match[1];
72
+ }
73
+ }
74
+ if (!resolvedVersion || !asset) {
75
+ throw new Error(`could not resolve latest release asset for ${osName}/${arch}`);
76
+ }
77
+
78
+ const archive = await requestBuffer(`${base}/${asset}`);
79
+ const line = checksums.toString("utf8").split(/\r?\n/).find((item) => item.endsWith(` ${asset}`));
80
+ if (!line) throw new Error(`checksum not found for ${asset}`);
81
+ const expected = line.split(/\s+/)[0].toLowerCase();
82
+ const actual = sha256(archive);
83
+ if (actual !== expected) throw new Error(`checksum mismatch for ${asset}`);
84
+
85
+ const temp = fs.mkdtempSync(path.join(os.tmpdir(), "dida-npm-"));
86
+ try {
87
+ const archivePath = path.join(temp, asset);
88
+ fs.writeFileSync(archivePath, archive);
89
+ if (ext === "zip") {
90
+ execFileSync("powershell", ["-NoProfile", "-Command", `Expand-Archive -LiteralPath '${archivePath.replace(/'/g, "''")}' -DestinationPath '${temp.replace(/'/g, "''")}' -Force`], { stdio: "inherit" });
91
+ } else {
92
+ execFileSync("tar", ["-xzf", archivePath, "-C", temp], { stdio: "inherit" });
93
+ }
94
+ const found = findFile(temp, exe);
95
+ if (!found) throw new Error("binary not found in archive");
96
+ fs.mkdirSync(binDir, { recursive: true });
97
+ const target = path.join(binDir, installedExe);
98
+ fs.copyFileSync(found, target);
99
+ if (osName !== "windows") fs.chmodSync(target, 0o755);
100
+ } finally {
101
+ fs.rmSync(temp, { recursive: true, force: true });
102
+ }
103
+ }
104
+
105
+ function findFile(dir, fileName) {
106
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
107
+ const full = path.join(dir, entry.name);
108
+ if (entry.isDirectory()) {
109
+ const hit = findFile(full, fileName);
110
+ if (hit) return hit;
111
+ } else if (entry.name === fileName) {
112
+ return full;
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+
118
+ main().catch((error) => {
119
+ console.error(`dida-cli install failed: ${error.message}`);
120
+ process.exit(1);
121
+ });