@fanzie/task-cli 0.1.1 → 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/dist/cli.js +374 -220
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command as e } from "commander";
|
|
3
|
-
import * as t from "node
|
|
3
|
+
import * as t from "@larksuiteoapi/node-sdk";
|
|
4
4
|
import * as n from "node:fs";
|
|
5
5
|
import * as r from "node:path";
|
|
6
6
|
import * as i from "node:os";
|
|
7
7
|
import * as a from "js-yaml";
|
|
8
|
-
import * as o from "@larksuiteoapi/node-sdk";
|
|
9
8
|
//#region src/config.ts
|
|
10
|
-
var
|
|
11
|
-
function
|
|
9
|
+
var o = r.join(i.homedir(), ".config", "fz-task"), s = r.join(o, "config.yaml"), c = r.join(o, ".env");
|
|
10
|
+
function l(e) {
|
|
12
11
|
let t = new URL(e), n = t.pathname.split("/").filter(Boolean), r = n.indexOf("wiki"), i = n.indexOf("base"), a = r === -1 ? i : r;
|
|
13
12
|
if (a === -1 || a + 1 >= n.length) throw Error(`Invalid bitable URL: cannot extract appToken from ${e}`);
|
|
14
|
-
let o = n[a + 1], s = t.searchParams.get("table");
|
|
15
|
-
if (!s) throw Error(`Invalid bitable URL: missing table parameter in ${e}`);
|
|
16
13
|
return {
|
|
17
|
-
appToken:
|
|
18
|
-
tableId:
|
|
14
|
+
appToken: n[a + 1],
|
|
15
|
+
tableId: t.searchParams.get("table") ?? void 0
|
|
19
16
|
};
|
|
20
17
|
}
|
|
21
|
-
function
|
|
18
|
+
function u(e, t) {
|
|
22
19
|
return e.replace(/\$\{(\w+)\}/g, (e, n) => t[n] ?? e);
|
|
23
20
|
}
|
|
24
|
-
function
|
|
25
|
-
let t = e ??
|
|
21
|
+
function d(e) {
|
|
22
|
+
let t = e ?? c, r = {};
|
|
26
23
|
if (!n.existsSync(t)) return r;
|
|
27
24
|
let i = n.readFileSync(t, "utf-8");
|
|
28
25
|
for (let e of i.split("\n")) {
|
|
@@ -35,63 +32,195 @@ function f(e) {
|
|
|
35
32
|
}
|
|
36
33
|
return r;
|
|
37
34
|
}
|
|
38
|
-
function
|
|
39
|
-
let e =
|
|
35
|
+
function f() {
|
|
36
|
+
let e = d(r.join(process.cwd(), ".env")), t = d(), i = {
|
|
40
37
|
...process.env,
|
|
41
38
|
...t,
|
|
42
39
|
...e
|
|
43
40
|
};
|
|
44
|
-
if (!n.existsSync(
|
|
45
|
-
let o = n.readFileSync(
|
|
46
|
-
if (!
|
|
41
|
+
if (!n.existsSync(s)) return null;
|
|
42
|
+
let o = n.readFileSync(s, "utf-8"), c = a.load(o);
|
|
43
|
+
if (!c?.profiles) return null;
|
|
47
44
|
let l = {
|
|
48
45
|
profiles: {},
|
|
49
|
-
defaultProfile:
|
|
46
|
+
defaultProfile: c.defaultProfile ?? "default"
|
|
50
47
|
};
|
|
51
|
-
for (let [e, t] of Object.entries(
|
|
48
|
+
for (let [e, t] of Object.entries(c.profiles)) {
|
|
52
49
|
let n = t;
|
|
53
50
|
l.profiles[e] = {
|
|
54
51
|
auth: {
|
|
55
|
-
appId:
|
|
56
|
-
appSecret:
|
|
52
|
+
appId: u(n.auth?.appId ?? "", i),
|
|
53
|
+
appSecret: u(n.auth?.appSecret ?? "", i)
|
|
57
54
|
},
|
|
58
55
|
bitable: {
|
|
59
56
|
url: n.bitable?.url ?? "",
|
|
60
57
|
appToken: n.bitable?.appToken ?? "",
|
|
61
|
-
tableId: n.bitable?.tableId
|
|
58
|
+
tableId: n.bitable?.tableId || void 0
|
|
62
59
|
}
|
|
63
60
|
};
|
|
64
61
|
}
|
|
65
62
|
return l;
|
|
66
63
|
}
|
|
67
|
-
function
|
|
64
|
+
function p(e, t) {
|
|
68
65
|
let n = t ?? process.env.FZ_TASK_PROFILE ?? e.defaultProfile, r = e.profiles[n];
|
|
69
66
|
if (!r) throw Error(`Profile "${n}" not found. Available: ${Object.keys(e.profiles).join(", ")}`);
|
|
70
67
|
return r;
|
|
71
68
|
}
|
|
72
|
-
function
|
|
73
|
-
n.mkdirSync(
|
|
69
|
+
function m() {
|
|
70
|
+
n.mkdirSync(o, { recursive: !0 });
|
|
74
71
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
72
|
+
function h(e) {
|
|
73
|
+
m();
|
|
77
74
|
let t = a.dump(e);
|
|
78
|
-
n.writeFileSync(
|
|
75
|
+
n.writeFileSync(s, t, "utf-8");
|
|
79
76
|
}
|
|
80
|
-
function
|
|
81
|
-
|
|
77
|
+
function g(e, t) {
|
|
78
|
+
m();
|
|
82
79
|
let r = `FZ_TASK_APP_ID=${e}\nFZ_TASK_APP_SECRET=${t}\n`;
|
|
83
|
-
n.writeFileSync(
|
|
80
|
+
n.writeFileSync(c, r, "utf-8");
|
|
81
|
+
}
|
|
82
|
+
var _ = ".fz-task.yaml";
|
|
83
|
+
function v(e) {
|
|
84
|
+
let t = e ?? process.cwd(), i = r.join(t, _);
|
|
85
|
+
if (!n.existsSync(i)) return null;
|
|
86
|
+
try {
|
|
87
|
+
let e = n.readFileSync(i, "utf-8"), t = a.load(e);
|
|
88
|
+
return t?.tableId ? {
|
|
89
|
+
tableId: t.tableId,
|
|
90
|
+
appToken: t.appToken || void 0
|
|
91
|
+
} : null;
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function y(e, t) {
|
|
97
|
+
let i = t ?? process.cwd(), o = r.join(i, _), s = { tableId: e.tableId };
|
|
98
|
+
e.appToken && (s.appToken = e.appToken);
|
|
99
|
+
let c = a.dump(s);
|
|
100
|
+
n.writeFileSync(o, c, "utf-8");
|
|
101
|
+
}
|
|
102
|
+
function b(e, t) {
|
|
103
|
+
let n = f();
|
|
104
|
+
if (!n) throw Error("No config found. Run `fz-task-cli auth init` to set up.");
|
|
105
|
+
let r = p(n, e), i = r.bitable.appToken, a, o = v(t);
|
|
106
|
+
if (o) a = o.tableId, o.appToken && (i = o.appToken);
|
|
107
|
+
else if (r.bitable.tableId) a = r.bitable.tableId, console.warn("Warning: Using tableId from global config. Run `fz-task init` to create a project config.");
|
|
108
|
+
else throw Error("No project config found. Run `fz-task init` in your project directory.");
|
|
109
|
+
return {
|
|
110
|
+
appId: r.auth.appId,
|
|
111
|
+
appSecret: r.auth.appSecret,
|
|
112
|
+
appToken: i,
|
|
113
|
+
tableId: a
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/commands/auth.ts
|
|
118
|
+
function x(e) {
|
|
119
|
+
let n = e.command("auth").description("Authentication management");
|
|
120
|
+
n.command("status").description("Show current global auth config status").action(async () => {
|
|
121
|
+
let t = f();
|
|
122
|
+
t || (console.error("No config found. Run `fz-task-cli auth init` to set up."), process.exit(3));
|
|
123
|
+
try {
|
|
124
|
+
let n = e.opts().profile ?? t.defaultProfile, r = p(t, e.opts().profile);
|
|
125
|
+
console.log(`Profile: ${n}`), console.log(`App ID: ${r.auth.appId.slice(0, 6)}...${r.auth.appId.slice(-4)}`), console.log(`App Token: ${r.bitable.appToken}`), console.log("Status: OK");
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error(e.message), process.exit(3);
|
|
128
|
+
}
|
|
129
|
+
}), n.command("init").description("Set up global authentication").requiredOption("--app-id <id>", "Feishu App ID").requiredOption("--app-secret <secret>", "Feishu App Secret").option("--url <url>", "Bitable URL (extracts appToken)").option("--app-token <token>", "Direct appToken (Base ID)").option("--no-verify", "Skip connection verification").action(async (e) => {
|
|
130
|
+
let { appId: n, appSecret: r, url: i, appToken: a, verify: o } = e;
|
|
131
|
+
i && a && (console.error("Cannot specify both --url and --app-token."), process.exit(2)), !i && !a && (console.error("Must specify either --url or --app-token."), process.exit(2));
|
|
132
|
+
let s;
|
|
133
|
+
if (i) try {
|
|
134
|
+
({appToken: s} = l(i));
|
|
135
|
+
} catch (e) {
|
|
136
|
+
console.error(`URL parsing failed: ${e.message}`), process.exit(2);
|
|
137
|
+
}
|
|
138
|
+
else s = a;
|
|
139
|
+
if (h({
|
|
140
|
+
profiles: { default: {
|
|
141
|
+
auth: {
|
|
142
|
+
appId: "${FZ_TASK_APP_ID}",
|
|
143
|
+
appSecret: "${FZ_TASK_APP_SECRET}"
|
|
144
|
+
},
|
|
145
|
+
bitable: {
|
|
146
|
+
url: i ?? "",
|
|
147
|
+
appToken: s
|
|
148
|
+
}
|
|
149
|
+
} },
|
|
150
|
+
defaultProfile: "default"
|
|
151
|
+
}), g(n, r), console.log("Config written to ~/.config/fz-task/config.yaml"), console.log("Credentials written to ~/.config/fz-task/.env"), console.log(`App Token: ${s}`), o) {
|
|
152
|
+
console.log("Verifying connection...");
|
|
153
|
+
try {
|
|
154
|
+
await new t.Client({
|
|
155
|
+
appId: n,
|
|
156
|
+
appSecret: r,
|
|
157
|
+
appType: t.AppType.SelfBuild,
|
|
158
|
+
domain: t.Domain.Feishu
|
|
159
|
+
}).bitable.appTable.list({ path: { app_token: s } }), console.log("Connection verified successfully!");
|
|
160
|
+
} catch (e) {
|
|
161
|
+
console.error(`Connection failed: ${e.message}`), process.exit(3);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
console.log("Run `fz-task init` in your project directory to initialize a project.");
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/types.ts
|
|
169
|
+
var S = [
|
|
170
|
+
"TODO",
|
|
171
|
+
"TEST_SPEC",
|
|
172
|
+
"DOING",
|
|
173
|
+
"TESTING",
|
|
174
|
+
"VERIFIED",
|
|
175
|
+
"DONE"
|
|
176
|
+
], C = new Map(S.map((e, t) => [e, t]));
|
|
177
|
+
function w(e, t) {
|
|
178
|
+
if (e === "DONE") return !1;
|
|
179
|
+
if (t === "BLOCKED" || e === "BLOCKED") return !0;
|
|
180
|
+
let n = C.get(e), r = C.get(t);
|
|
181
|
+
return n === void 0 || r === void 0 ? !1 : r === n + 1 || r === n - 1;
|
|
182
|
+
}
|
|
183
|
+
function T(e) {
|
|
184
|
+
if (e === "DONE") return [];
|
|
185
|
+
if (e === "BLOCKED") return [...S];
|
|
186
|
+
let t = C.get(e);
|
|
187
|
+
if (t === void 0) return [];
|
|
188
|
+
let n = [];
|
|
189
|
+
return t > 0 && n.push(S[t - 1]), t < S.length - 1 && n.push(S[t + 1]), n.push("BLOCKED"), n;
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/formatter.ts
|
|
193
|
+
function E(e, t, n) {
|
|
194
|
+
if (n === "json") return JSON.stringify({
|
|
195
|
+
...e,
|
|
196
|
+
children: t
|
|
197
|
+
}, null, 2);
|
|
198
|
+
let r = [];
|
|
199
|
+
if (r.push(`ID: ${e.record_id}`), r.push(`Title: ${e.fields.title}`), r.push(`Type: ${e.fields.type}`), r.push(`Status: ${e.fields.status}`), e.fields.priority && r.push(`Priority: ${e.fields.priority}`), e.fields.assignee && r.push(`Assignee: ${e.fields.assignee}`), e.fields.doc_url && r.push(`Doc URL: ${e.fields.doc_url}`), e.fields.description && r.push(`Description: ${e.fields.description}`), t.length > 0) {
|
|
200
|
+
let n = e.fields.type === "Feature" ? "Stories" : "ACs";
|
|
201
|
+
r.push(""), r.push(`Children (${t.length} ${n}):`);
|
|
202
|
+
for (let e of t) r.push(` ${e.record_id} [${e.fields.status}] ${e.fields.title}`);
|
|
203
|
+
}
|
|
204
|
+
return r.join("\n");
|
|
205
|
+
}
|
|
206
|
+
function D(e, t) {
|
|
207
|
+
if (t === "json") return JSON.stringify(e, null, 2);
|
|
208
|
+
if (e.length === 0) return "No records found.";
|
|
209
|
+
let n = [];
|
|
210
|
+
n.push("ID Type Status Priority Title"), n.push("-".repeat(80));
|
|
211
|
+
for (let t of e) n.push(`${t.record_id}\t${t.fields.type}\t${t.fields.status}\t${t.fields.priority ?? "-"}\t${t.fields.title}`);
|
|
212
|
+
return n.join("\n");
|
|
84
213
|
}
|
|
85
214
|
//#endregion
|
|
86
215
|
//#region src/client.ts
|
|
87
|
-
function
|
|
216
|
+
function O(e) {
|
|
88
217
|
let t = {};
|
|
89
218
|
return e.title !== void 0 && (t.Text = e.title), e.type !== void 0 && (t.type = e.type), e.status !== void 0 && (t.status = e.status), e.parent !== void 0 && (t.parent = e.parent), e.doc_url !== void 0 && (t.doc_url = {
|
|
90
219
|
link: e.doc_url,
|
|
91
220
|
text: e.doc_url
|
|
92
221
|
}), e.assignee !== void 0 && (t.assignee = e.assignee), e.description !== void 0 && (t.description = e.description), e.previous_status !== void 0 && (t.previous_status = e.previous_status), e.priority !== void 0 && (t.priority = e.priority), t;
|
|
93
222
|
}
|
|
94
|
-
function
|
|
223
|
+
function k(e) {
|
|
95
224
|
let t = e.fields, n, r = t.parent;
|
|
96
225
|
if (Array.isArray(r) && r.length > 0) {
|
|
97
226
|
let e = r[0];
|
|
@@ -113,20 +242,20 @@ function y(e) {
|
|
|
113
242
|
}
|
|
114
243
|
};
|
|
115
244
|
}
|
|
116
|
-
var
|
|
245
|
+
var A = class {
|
|
117
246
|
larkClient;
|
|
118
247
|
appToken;
|
|
119
248
|
tableId;
|
|
120
249
|
constructor(e) {
|
|
121
|
-
this.appToken = e.appToken, this.tableId = e.tableId, this.larkClient = new
|
|
250
|
+
this.appToken = e.appToken, this.tableId = e.tableId, this.larkClient = new t.Client({
|
|
122
251
|
appId: e.appId,
|
|
123
252
|
appSecret: e.appSecret,
|
|
124
|
-
appType:
|
|
125
|
-
domain:
|
|
253
|
+
appType: t.AppType.SelfBuild,
|
|
254
|
+
domain: t.Domain.Feishu
|
|
126
255
|
});
|
|
127
256
|
}
|
|
128
257
|
async createRecord(e) {
|
|
129
|
-
let t =
|
|
258
|
+
let t = O(e), n = await this.larkClient.bitable.appTableRecord.create({
|
|
130
259
|
path: {
|
|
131
260
|
app_token: this.appToken,
|
|
132
261
|
table_id: this.tableId
|
|
@@ -136,7 +265,7 @@ var b = class {
|
|
|
136
265
|
if (n.code !== 0) throw Error(`createRecord failed: ${n.msg}`);
|
|
137
266
|
let r = n.data?.record;
|
|
138
267
|
if (!r) throw Error("createRecord: no record in response");
|
|
139
|
-
return
|
|
268
|
+
return k(r);
|
|
140
269
|
}
|
|
141
270
|
async getRecord(e) {
|
|
142
271
|
let t = await this.larkClient.bitable.appTableRecord.get({ path: {
|
|
@@ -147,7 +276,7 @@ var b = class {
|
|
|
147
276
|
if (t.code !== 0) throw Error(`getRecord failed: ${t.msg}`);
|
|
148
277
|
let n = t.data?.record;
|
|
149
278
|
if (!n) throw Error("getRecord: no record in response");
|
|
150
|
-
return
|
|
279
|
+
return k(n);
|
|
151
280
|
}
|
|
152
281
|
async listRecords(e) {
|
|
153
282
|
let t = [];
|
|
@@ -165,13 +294,13 @@ var b = class {
|
|
|
165
294
|
});
|
|
166
295
|
if (t.code !== 0) throw Error(`listRecords failed: ${t.msg}`);
|
|
167
296
|
let a = t.data?.items ?? [];
|
|
168
|
-
for (let e of a) r.push(
|
|
297
|
+
for (let e of a) r.push(k(e));
|
|
169
298
|
i = t.data?.has_more ? t.data.page_token : void 0;
|
|
170
299
|
} while (i);
|
|
171
300
|
return r;
|
|
172
301
|
}
|
|
173
302
|
async updateRecord(e, t) {
|
|
174
|
-
let n =
|
|
303
|
+
let n = O(t), r = await this.larkClient.bitable.appTableRecord.update({
|
|
175
304
|
path: {
|
|
176
305
|
app_token: this.appToken,
|
|
177
306
|
table_id: this.tableId,
|
|
@@ -182,7 +311,7 @@ var b = class {
|
|
|
182
311
|
if (r.code !== 0) throw Error(`updateRecord failed: ${r.msg}`);
|
|
183
312
|
let i = r.data?.record;
|
|
184
313
|
if (!i) throw Error("updateRecord: no record in response");
|
|
185
|
-
return
|
|
314
|
+
return k(i);
|
|
186
315
|
}
|
|
187
316
|
async deleteRecord(e) {
|
|
188
317
|
let t = await this.larkClient.bitable.appTableRecord.delete({ path: {
|
|
@@ -197,129 +326,23 @@ var b = class {
|
|
|
197
326
|
}
|
|
198
327
|
};
|
|
199
328
|
//#endregion
|
|
200
|
-
//#region src/commands/
|
|
201
|
-
function
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return new Promise((t) => {
|
|
207
|
-
n.question(e, (e) => {
|
|
208
|
-
n.close(), t(e.trim());
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
function S(e) {
|
|
213
|
-
let t = e.command("auth").description("Authentication management");
|
|
214
|
-
t.command("status").description("Show current config and connection status").action(async () => {
|
|
215
|
-
let t = p();
|
|
216
|
-
t || (console.error("No config found. Run `fz-task-cli auth init` to set up."), process.exit(3));
|
|
217
|
-
try {
|
|
218
|
-
let n = e.opts().profile ?? t.defaultProfile, r = m(t, e.opts().profile);
|
|
219
|
-
console.log(`Profile: ${n}`), console.log(`App ID: ${r.auth.appId.slice(0, 6)}...${r.auth.appId.slice(-4)}`), console.log(`App Token: ${r.bitable.appToken}`), console.log(`Table ID: ${r.bitable.tableId}`), console.log("Status: OK");
|
|
220
|
-
} catch (e) {
|
|
221
|
-
console.error(e.message), process.exit(3);
|
|
222
|
-
}
|
|
223
|
-
}), t.command("init").description("Interactive setup").action(async () => {
|
|
224
|
-
let e = await x("Feishu App ID: "), t = await x("Feishu App Secret: "), n = await x("Bitable URL: "), r, i;
|
|
225
|
-
try {
|
|
226
|
-
({appToken: r, tableId: i} = u(n));
|
|
227
|
-
} catch (e) {
|
|
228
|
-
console.error(`URL parsing failed: ${e.message}`), process.exit(2);
|
|
229
|
-
}
|
|
230
|
-
g({
|
|
231
|
-
profiles: { default: {
|
|
232
|
-
auth: {
|
|
233
|
-
appId: "${FZ_TASK_APP_ID}",
|
|
234
|
-
appSecret: "${FZ_TASK_APP_SECRET}"
|
|
235
|
-
},
|
|
236
|
-
bitable: {
|
|
237
|
-
url: n,
|
|
238
|
-
appToken: r,
|
|
239
|
-
tableId: i
|
|
240
|
-
}
|
|
241
|
-
} },
|
|
242
|
-
defaultProfile: "default"
|
|
243
|
-
}), _(e, t), console.log("Config written to ~/.config/fz-task/config.yaml"), console.log("Credentials written to ~/.config/fz-task/.env"), console.log(`App Token: ${r}`), console.log(`Table ID: ${i}`), console.log("Verifying connection...");
|
|
244
|
-
try {
|
|
245
|
-
await new b({
|
|
246
|
-
appId: e,
|
|
247
|
-
appSecret: t,
|
|
248
|
-
appToken: r,
|
|
249
|
-
tableId: i
|
|
250
|
-
}).listRecords({ type: "Feature" }), console.log("Connection verified successfully!");
|
|
251
|
-
} catch (e) {
|
|
252
|
-
console.error(`Connection failed: ${e.message}`), process.exit(3);
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
//#endregion
|
|
257
|
-
//#region src/types.ts
|
|
258
|
-
var C = [
|
|
259
|
-
"TODO",
|
|
260
|
-
"TEST_SPEC",
|
|
261
|
-
"DOING",
|
|
262
|
-
"TESTING",
|
|
263
|
-
"VERIFIED",
|
|
264
|
-
"DONE"
|
|
265
|
-
], w = new Map(C.map((e, t) => [e, t]));
|
|
266
|
-
function T(e, t) {
|
|
267
|
-
if (e === "DONE") return !1;
|
|
268
|
-
if (t === "BLOCKED" || e === "BLOCKED") return !0;
|
|
269
|
-
let n = w.get(e), r = w.get(t);
|
|
270
|
-
return n === void 0 || r === void 0 ? !1 : r === n + 1 || r === n - 1;
|
|
271
|
-
}
|
|
272
|
-
function E(e) {
|
|
273
|
-
if (e === "DONE") return [];
|
|
274
|
-
if (e === "BLOCKED") return [...C];
|
|
275
|
-
let t = w.get(e);
|
|
276
|
-
if (t === void 0) return [];
|
|
277
|
-
let n = [];
|
|
278
|
-
return t > 0 && n.push(C[t - 1]), t < C.length - 1 && n.push(C[t + 1]), n.push("BLOCKED"), n;
|
|
279
|
-
}
|
|
280
|
-
//#endregion
|
|
281
|
-
//#region src/formatter.ts
|
|
282
|
-
function D(e, t, n) {
|
|
283
|
-
if (n === "json") return JSON.stringify({
|
|
284
|
-
...e,
|
|
285
|
-
children: t
|
|
286
|
-
}, null, 2);
|
|
287
|
-
let r = [];
|
|
288
|
-
if (r.push(`ID: ${e.record_id}`), r.push(`Title: ${e.fields.title}`), r.push(`Type: ${e.fields.type}`), r.push(`Status: ${e.fields.status}`), e.fields.priority && r.push(`Priority: ${e.fields.priority}`), e.fields.assignee && r.push(`Assignee: ${e.fields.assignee}`), e.fields.doc_url && r.push(`Doc URL: ${e.fields.doc_url}`), e.fields.description && r.push(`Description: ${e.fields.description}`), t.length > 0) {
|
|
289
|
-
let n = e.fields.type === "Feature" ? "Stories" : "ACs";
|
|
290
|
-
r.push(""), r.push(`Children (${t.length} ${n}):`);
|
|
291
|
-
for (let e of t) r.push(` ${e.record_id} [${e.fields.status}] ${e.fields.title}`);
|
|
329
|
+
//#region src/commands/shared.ts
|
|
330
|
+
function j(e) {
|
|
331
|
+
try {
|
|
332
|
+
return new A(b(e.parent?.opts().profile));
|
|
333
|
+
} catch (e) {
|
|
334
|
+
console.error(e.message), process.exit(3);
|
|
292
335
|
}
|
|
293
|
-
return r.join("\n");
|
|
294
336
|
}
|
|
295
|
-
function
|
|
296
|
-
|
|
297
|
-
if (e.length === 0) return "No records found.";
|
|
298
|
-
let n = [];
|
|
299
|
-
n.push("ID Type Status Priority Title"), n.push("-".repeat(80));
|
|
300
|
-
for (let t of e) n.push(`${t.record_id}\t${t.fields.type}\t${t.fields.status}\t${t.fields.priority ?? "-"}\t${t.fields.title}`);
|
|
301
|
-
return n.join("\n");
|
|
337
|
+
function M(e) {
|
|
338
|
+
return e.parent?.opts().format ?? "table";
|
|
302
339
|
}
|
|
303
340
|
//#endregion
|
|
304
341
|
//#region src/commands/feature.ts
|
|
305
|
-
function
|
|
306
|
-
let t = p();
|
|
307
|
-
t || (console.error("No config found. Run `fz-task-cli auth init` to set up."), process.exit(3));
|
|
308
|
-
let n = m(t, e.parent?.opts().profile);
|
|
309
|
-
return new b({
|
|
310
|
-
appId: n.auth.appId,
|
|
311
|
-
appSecret: n.auth.appSecret,
|
|
312
|
-
appToken: n.bitable.appToken,
|
|
313
|
-
tableId: n.bitable.tableId
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
function A(e) {
|
|
317
|
-
return e.parent?.opts().format ?? "table";
|
|
318
|
-
}
|
|
319
|
-
function j(e) {
|
|
342
|
+
function N(e) {
|
|
320
343
|
let t = e.command("feature").description("Feature management commands");
|
|
321
344
|
t.command("add [title]").description("Create a new Feature").option("--json <json>", "JSON string with fields").action(async (e, n, r) => {
|
|
322
|
-
let i =
|
|
345
|
+
let i = j(t), a = M(t), o = {};
|
|
323
346
|
if (n.json) try {
|
|
324
347
|
o = JSON.parse(n.json);
|
|
325
348
|
} catch {
|
|
@@ -328,20 +351,20 @@ function j(e) {
|
|
|
328
351
|
e && (o.title = e), o.title || (console.error("Title is required. Provide as argument or in --json."), process.exit(2)), o.type = "Feature", o.status ||= "TODO";
|
|
329
352
|
try {
|
|
330
353
|
let e = await i.createRecord(o);
|
|
331
|
-
console.log(
|
|
354
|
+
console.log(E(e, [], a));
|
|
332
355
|
} catch (e) {
|
|
333
356
|
console.error(`Failed to create feature: ${e.message}`), process.exit(1);
|
|
334
357
|
}
|
|
335
358
|
}), t.command("list").description("List all Features").action(async (e, n) => {
|
|
336
|
-
let r =
|
|
359
|
+
let r = j(t), i = M(t);
|
|
337
360
|
try {
|
|
338
361
|
let e = await r.listRecords({ type: "Feature" });
|
|
339
|
-
console.log(
|
|
362
|
+
console.log(D(e, i));
|
|
340
363
|
} catch (e) {
|
|
341
364
|
console.error(`Failed to list features: ${e.message}`), process.exit(1);
|
|
342
365
|
}
|
|
343
366
|
}), t.command("get <id>").description("Get a Feature by ID").action(async (e, n, r) => {
|
|
344
|
-
let i =
|
|
367
|
+
let i = j(t), a = M(t), o;
|
|
345
368
|
try {
|
|
346
369
|
o = await i.getRecord(e);
|
|
347
370
|
} catch (e) {
|
|
@@ -350,12 +373,12 @@ function j(e) {
|
|
|
350
373
|
o.fields.type !== "Feature" && (console.error(`Record ${e} is not a Feature (type: ${o.fields.type}).`), process.exit(2));
|
|
351
374
|
try {
|
|
352
375
|
let t = await i.getChildren(e);
|
|
353
|
-
console.log(
|
|
376
|
+
console.log(E(o, t, a));
|
|
354
377
|
} catch (e) {
|
|
355
378
|
console.error(`Failed to get children: ${e.message}`), process.exit(1);
|
|
356
379
|
}
|
|
357
380
|
}), t.command("update <id>").description("Update a Feature").option("--status <status>", "New status").option("--priority <priority>", "New priority").option("--assignee <assignee>", "New assignee").option("--doc-url <url>", "New doc URL").option("--title <title>", "New title").option("--description <description>", "New description").action(async (e, n, r) => {
|
|
358
|
-
let i =
|
|
381
|
+
let i = j(t), a = M(t), o;
|
|
359
382
|
try {
|
|
360
383
|
o = await i.getRecord(e);
|
|
361
384
|
} catch (e) {
|
|
@@ -365,8 +388,8 @@ function j(e) {
|
|
|
365
388
|
let s = {};
|
|
366
389
|
if (n.status) {
|
|
367
390
|
let e = n.status, t = o.fields.status;
|
|
368
|
-
if (!
|
|
369
|
-
let n =
|
|
391
|
+
if (!w(t, e)) {
|
|
392
|
+
let n = T(t);
|
|
370
393
|
console.error(`Invalid status transition: ${t} -> ${e}. Allowed: ${n.join(", ")}`), process.exit(2);
|
|
371
394
|
}
|
|
372
395
|
s.status = e, e === "BLOCKED" && (s.previous_status = t), t === "BLOCKED" && (s.previous_status = void 0);
|
|
@@ -374,12 +397,12 @@ function j(e) {
|
|
|
374
397
|
n.priority && (s.priority = n.priority), n.assignee && (s.assignee = n.assignee), n.docUrl && (s.doc_url = n.docUrl), n.title && (s.title = n.title), n.description && (s.description = n.description);
|
|
375
398
|
try {
|
|
376
399
|
let t = await i.updateRecord(e, s), n = await i.getChildren(e);
|
|
377
|
-
console.log(
|
|
400
|
+
console.log(E(t, n, a));
|
|
378
401
|
} catch (e) {
|
|
379
402
|
console.error(`Failed to update feature: ${e.message}`), process.exit(1);
|
|
380
403
|
}
|
|
381
404
|
}), t.command("delete <id>").description("Delete a Feature").action(async (e, n, r) => {
|
|
382
|
-
let i =
|
|
405
|
+
let i = j(t), a;
|
|
383
406
|
try {
|
|
384
407
|
a = await i.getRecord(e);
|
|
385
408
|
} catch (e) {
|
|
@@ -397,24 +420,10 @@ function j(e) {
|
|
|
397
420
|
}
|
|
398
421
|
//#endregion
|
|
399
422
|
//#region src/commands/story.ts
|
|
400
|
-
function M(e) {
|
|
401
|
-
let t = p();
|
|
402
|
-
t || (console.error("No config found. Run `fz-task-cli auth init` to set up."), process.exit(3));
|
|
403
|
-
let n = m(t, e.parent?.opts().profile);
|
|
404
|
-
return new b({
|
|
405
|
-
appId: n.auth.appId,
|
|
406
|
-
appSecret: n.auth.appSecret,
|
|
407
|
-
appToken: n.bitable.appToken,
|
|
408
|
-
tableId: n.bitable.tableId
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
function N(e) {
|
|
412
|
-
return e.parent?.opts().format ?? "table";
|
|
413
|
-
}
|
|
414
423
|
function P(e) {
|
|
415
424
|
let t = e.command("story").description("UserStory management commands");
|
|
416
425
|
t.command("add [title]").description("Create a new UserStory under a Feature").requiredOption("--feature <id>", "Parent Feature record ID").option("--json <json>", "JSON string with fields").action(async (e, n, r) => {
|
|
417
|
-
let i =
|
|
426
|
+
let i = j(t), a = M(t), o;
|
|
418
427
|
try {
|
|
419
428
|
o = await i.getRecord(n.feature);
|
|
420
429
|
} catch (e) {
|
|
@@ -430,20 +439,20 @@ function P(e) {
|
|
|
430
439
|
e && (s.title = e), s.title || (console.error("Title is required. Provide as argument or in --json."), process.exit(2)), s.type = "UserStory", s.parent = [n.feature], s.status ||= "TODO";
|
|
431
440
|
try {
|
|
432
441
|
let e = await i.createRecord(s);
|
|
433
|
-
console.log(
|
|
442
|
+
console.log(E(e, [], a));
|
|
434
443
|
} catch (e) {
|
|
435
444
|
console.error(`Failed to create story: ${e.message}`), process.exit(1);
|
|
436
445
|
}
|
|
437
446
|
}), t.command("list").description("List UserStories, optionally filtered by Feature").option("--feature <id>", "Parent Feature record ID to filter by").action(async (e, n) => {
|
|
438
|
-
let r =
|
|
447
|
+
let r = j(t), i = M(t);
|
|
439
448
|
try {
|
|
440
449
|
let t;
|
|
441
|
-
t = e.feature ? (await r.getChildren(e.feature)).filter((e) => e.fields.type === "UserStory") : await r.listRecords({ type: "UserStory" }), console.log(
|
|
450
|
+
t = e.feature ? (await r.getChildren(e.feature)).filter((e) => e.fields.type === "UserStory") : await r.listRecords({ type: "UserStory" }), console.log(D(t, i));
|
|
442
451
|
} catch (e) {
|
|
443
452
|
console.error(`Failed to list stories: ${e.message}`), process.exit(1);
|
|
444
453
|
}
|
|
445
454
|
}), t.command("get <id>").description("Get a UserStory by ID").action(async (e, n, r) => {
|
|
446
|
-
let i =
|
|
455
|
+
let i = j(t), a = M(t), o;
|
|
447
456
|
try {
|
|
448
457
|
o = await i.getRecord(e);
|
|
449
458
|
} catch (e) {
|
|
@@ -452,12 +461,12 @@ function P(e) {
|
|
|
452
461
|
o.fields.type !== "UserStory" && (console.error(`Record ${e} is not a UserStory (type: ${o.fields.type}).`), process.exit(2));
|
|
453
462
|
try {
|
|
454
463
|
let t = await i.getChildren(e);
|
|
455
|
-
console.log(
|
|
464
|
+
console.log(E(o, t, a));
|
|
456
465
|
} catch (e) {
|
|
457
466
|
console.error(`Failed to get children: ${e.message}`), process.exit(1);
|
|
458
467
|
}
|
|
459
468
|
}), t.command("update <id>").description("Update a UserStory").option("--status <status>", "New status").option("--priority <priority>", "New priority").option("--assignee <assignee>", "New assignee").option("--doc-url <url>", "New doc URL").option("--title <title>", "New title").option("--description <description>", "New description").action(async (e, n, r) => {
|
|
460
|
-
let i =
|
|
469
|
+
let i = j(t), a = M(t), o;
|
|
461
470
|
try {
|
|
462
471
|
o = await i.getRecord(e);
|
|
463
472
|
} catch (e) {
|
|
@@ -467,8 +476,8 @@ function P(e) {
|
|
|
467
476
|
let s = {};
|
|
468
477
|
if (n.status) {
|
|
469
478
|
let e = n.status, t = o.fields.status;
|
|
470
|
-
if (!
|
|
471
|
-
let n =
|
|
479
|
+
if (!w(t, e)) {
|
|
480
|
+
let n = T(t);
|
|
472
481
|
console.error(`Invalid status transition: ${t} -> ${e}. Allowed: ${n.join(", ")}`), process.exit(2);
|
|
473
482
|
}
|
|
474
483
|
s.status = e, e === "BLOCKED" && (s.previous_status = t), t === "BLOCKED" && (s.previous_status = void 0);
|
|
@@ -476,12 +485,12 @@ function P(e) {
|
|
|
476
485
|
n.priority && (s.priority = n.priority), n.assignee && (s.assignee = n.assignee), n.docUrl && (s.doc_url = n.docUrl), n.title && (s.title = n.title), n.description && (s.description = n.description);
|
|
477
486
|
try {
|
|
478
487
|
let t = await i.updateRecord(e, s), n = await i.getChildren(e);
|
|
479
|
-
console.log(
|
|
488
|
+
console.log(E(t, n, a));
|
|
480
489
|
} catch (e) {
|
|
481
490
|
console.error(`Failed to update story: ${e.message}`), process.exit(1);
|
|
482
491
|
}
|
|
483
492
|
}), t.command("delete <id>").description("Delete a UserStory").action(async (e, n, r) => {
|
|
484
|
-
let i =
|
|
493
|
+
let i = j(t), a;
|
|
485
494
|
try {
|
|
486
495
|
a = await i.getRecord(e);
|
|
487
496
|
} catch (e) {
|
|
@@ -500,23 +509,9 @@ function P(e) {
|
|
|
500
509
|
//#endregion
|
|
501
510
|
//#region src/commands/ac.ts
|
|
502
511
|
function F(e) {
|
|
503
|
-
let t = p();
|
|
504
|
-
t || (console.error("No config found. Run `fz-task-cli auth init` to set up."), process.exit(3));
|
|
505
|
-
let n = m(t, e.parent?.opts().profile);
|
|
506
|
-
return new b({
|
|
507
|
-
appId: n.auth.appId,
|
|
508
|
-
appSecret: n.auth.appSecret,
|
|
509
|
-
appToken: n.bitable.appToken,
|
|
510
|
-
tableId: n.bitable.tableId
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
function I(e) {
|
|
514
|
-
return e.parent?.opts().format ?? "table";
|
|
515
|
-
}
|
|
516
|
-
function L(e) {
|
|
517
512
|
let t = e.command("ac").description("AC (Acceptance Criteria) management commands");
|
|
518
513
|
t.command("add [title]").description("Create a new AC under a UserStory").requiredOption("--story <id>", "Parent UserStory record ID").option("--json <json>", "JSON string with fields").action(async (e, n, r) => {
|
|
519
|
-
let i =
|
|
514
|
+
let i = j(t), a = M(t), o;
|
|
520
515
|
try {
|
|
521
516
|
o = await i.getRecord(n.story);
|
|
522
517
|
} catch (e) {
|
|
@@ -532,28 +527,28 @@ function L(e) {
|
|
|
532
527
|
e && (s.title = e), s.title || (console.error("Title is required. Provide as argument or in --json."), process.exit(2)), s.type = "AC", s.parent = [n.story], s.status ||= "TODO";
|
|
533
528
|
try {
|
|
534
529
|
let e = await i.createRecord(s);
|
|
535
|
-
console.log(
|
|
530
|
+
console.log(E(e, [], a));
|
|
536
531
|
} catch (e) {
|
|
537
532
|
console.error(`Failed to create AC: ${e.message}`), process.exit(1);
|
|
538
533
|
}
|
|
539
534
|
}), t.command("list").description("List ACs, optionally filtered by UserStory").option("--story <id>", "Parent UserStory record ID to filter by").action(async (e, n) => {
|
|
540
|
-
let r =
|
|
535
|
+
let r = j(t), i = M(t);
|
|
541
536
|
try {
|
|
542
537
|
let t;
|
|
543
|
-
t = e.story ? (await r.getChildren(e.story)).filter((e) => e.fields.type === "AC") : await r.listRecords({ type: "AC" }), console.log(
|
|
538
|
+
t = e.story ? (await r.getChildren(e.story)).filter((e) => e.fields.type === "AC") : await r.listRecords({ type: "AC" }), console.log(D(t, i));
|
|
544
539
|
} catch (e) {
|
|
545
540
|
console.error(`Failed to list ACs: ${e.message}`), process.exit(1);
|
|
546
541
|
}
|
|
547
542
|
}), t.command("get <id>").description("Get an AC by ID").action(async (e, n, r) => {
|
|
548
|
-
let i =
|
|
543
|
+
let i = j(t), a = M(t), o;
|
|
549
544
|
try {
|
|
550
545
|
o = await i.getRecord(e);
|
|
551
546
|
} catch (e) {
|
|
552
547
|
console.error(`Record not found: ${e.message}`), process.exit(4);
|
|
553
548
|
}
|
|
554
|
-
o.fields.type !== "AC" && (console.error(`Record ${e} is not an AC (type: ${o.fields.type}).`), process.exit(2)), console.log(
|
|
549
|
+
o.fields.type !== "AC" && (console.error(`Record ${e} is not an AC (type: ${o.fields.type}).`), process.exit(2)), console.log(E(o, [], a));
|
|
555
550
|
}), t.command("update <id>").description("Update an AC").option("--status <status>", "New status").option("--priority <priority>", "New priority").option("--assignee <assignee>", "New assignee").option("--doc-url <url>", "New doc URL").option("--title <title>", "New title").option("--description <description>", "New description").action(async (e, n, r) => {
|
|
556
|
-
let i =
|
|
551
|
+
let i = j(t), a = M(t), o;
|
|
557
552
|
try {
|
|
558
553
|
o = await i.getRecord(e);
|
|
559
554
|
} catch (e) {
|
|
@@ -563,8 +558,8 @@ function L(e) {
|
|
|
563
558
|
let s = {};
|
|
564
559
|
if (n.status) {
|
|
565
560
|
let e = n.status, t = o.fields.status;
|
|
566
|
-
if (!
|
|
567
|
-
let n =
|
|
561
|
+
if (!w(t, e)) {
|
|
562
|
+
let n = T(t);
|
|
568
563
|
console.error(`Invalid status transition: ${t} -> ${e}. Allowed: ${n.join(", ")}`), process.exit(2);
|
|
569
564
|
}
|
|
570
565
|
s.status = e, e === "BLOCKED" && (s.previous_status = t), t === "BLOCKED" && (s.previous_status = void 0);
|
|
@@ -572,12 +567,12 @@ function L(e) {
|
|
|
572
567
|
n.priority && (s.priority = n.priority), n.assignee && (s.assignee = n.assignee), n.docUrl && (s.doc_url = n.docUrl), n.title && (s.title = n.title), n.description && (s.description = n.description);
|
|
573
568
|
try {
|
|
574
569
|
let t = await i.updateRecord(e, s);
|
|
575
|
-
console.log(
|
|
570
|
+
console.log(E(t, [], a));
|
|
576
571
|
} catch (e) {
|
|
577
572
|
console.error(`Failed to update AC: ${e.message}`), process.exit(1);
|
|
578
573
|
}
|
|
579
574
|
}), t.command("delete <id>").description("Delete an AC").action(async (e, n, r) => {
|
|
580
|
-
let i =
|
|
575
|
+
let i = j(t), a;
|
|
581
576
|
try {
|
|
582
577
|
a = await i.getRecord(e);
|
|
583
578
|
} catch (e) {
|
|
@@ -592,7 +587,166 @@ function L(e) {
|
|
|
592
587
|
});
|
|
593
588
|
}
|
|
594
589
|
//#endregion
|
|
590
|
+
//#region src/commands/init.ts
|
|
591
|
+
var I = 1, L = 3, R = 15, z = 21;
|
|
592
|
+
function B() {
|
|
593
|
+
return [
|
|
594
|
+
{
|
|
595
|
+
field_name: "Text",
|
|
596
|
+
type: I,
|
|
597
|
+
ui_type: "Text"
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
field_name: "type",
|
|
601
|
+
type: L,
|
|
602
|
+
ui_type: "SingleSelect",
|
|
603
|
+
property: { options: [
|
|
604
|
+
{ name: "Feature" },
|
|
605
|
+
{ name: "UserStory" },
|
|
606
|
+
{ name: "AC" }
|
|
607
|
+
] }
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
field_name: "status",
|
|
611
|
+
type: L,
|
|
612
|
+
ui_type: "SingleSelect",
|
|
613
|
+
property: { options: [
|
|
614
|
+
{ name: "TODO" },
|
|
615
|
+
{ name: "TEST_SPEC" },
|
|
616
|
+
{ name: "DOING" },
|
|
617
|
+
{ name: "TESTING" },
|
|
618
|
+
{ name: "VERIFIED" },
|
|
619
|
+
{ name: "DONE" },
|
|
620
|
+
{ name: "BLOCKED" }
|
|
621
|
+
] }
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
field_name: "priority",
|
|
625
|
+
type: L,
|
|
626
|
+
ui_type: "SingleSelect",
|
|
627
|
+
property: { options: [
|
|
628
|
+
{ name: "P0" },
|
|
629
|
+
{ name: "P1" },
|
|
630
|
+
{ name: "P2" },
|
|
631
|
+
{ name: "P3" }
|
|
632
|
+
] }
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
field_name: "assignee",
|
|
636
|
+
type: I,
|
|
637
|
+
ui_type: "Text"
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
field_name: "description",
|
|
641
|
+
type: I,
|
|
642
|
+
ui_type: "Text"
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
field_name: "doc_url",
|
|
646
|
+
type: R,
|
|
647
|
+
ui_type: "Url"
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
field_name: "previous_status",
|
|
651
|
+
type: I,
|
|
652
|
+
ui_type: "Text"
|
|
653
|
+
}
|
|
654
|
+
];
|
|
655
|
+
}
|
|
656
|
+
function V(e) {
|
|
657
|
+
e.command("init").description("Initialize project — create or link a Bitable table").option("--url <url>", "Link an existing table by URL").option("--force", "Force reinitialize (recreates table)").action(async (n) => {
|
|
658
|
+
let r = e.opts().profile, i = f();
|
|
659
|
+
i || (console.error("No config found. Run `fz-task-cli auth init` to set up."), process.exit(3));
|
|
660
|
+
let a;
|
|
661
|
+
try {
|
|
662
|
+
a = p(i, r);
|
|
663
|
+
} catch (e) {
|
|
664
|
+
console.error(e.message), process.exit(3);
|
|
665
|
+
}
|
|
666
|
+
let o = a.bitable.appToken;
|
|
667
|
+
o || (console.error("No appToken in global config. Run `fz-task-cli auth init` to set up."), process.exit(3));
|
|
668
|
+
let s = new t.Client({
|
|
669
|
+
appId: a.auth.appId,
|
|
670
|
+
appSecret: a.auth.appSecret,
|
|
671
|
+
appType: t.AppType.SelfBuild,
|
|
672
|
+
domain: t.Domain.Feishu
|
|
673
|
+
});
|
|
674
|
+
n.url ? await H(n.url, o, n.force) : await U(s, o, n.force);
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
async function H(e, t, n) {
|
|
678
|
+
let r, i;
|
|
679
|
+
try {
|
|
680
|
+
let t = l(e);
|
|
681
|
+
r = t.appToken, i = t.tableId;
|
|
682
|
+
} catch (e) {
|
|
683
|
+
console.error(`URL parsing failed: ${e.message}`), process.exit(2);
|
|
684
|
+
}
|
|
685
|
+
i || (console.error("URL must contain a table parameter (e.g., ?table=tblXXX)."), process.exit(2)), v() && !n && (console.error("Already initialized. Use --force to reinitialize."), process.exit(2));
|
|
686
|
+
let a = { tableId: i };
|
|
687
|
+
r !== t && (a.appToken = r), y(a), console.log(`Project linked to existing table: ${i}`);
|
|
688
|
+
}
|
|
689
|
+
async function U(e, t, n) {
|
|
690
|
+
let i = v();
|
|
691
|
+
if (i && !n && (console.error("Already initialized. Use --force to reinitialize."), process.exit(2)), i && n) {
|
|
692
|
+
console.log(`Deleting old table: ${i.tableId}...`);
|
|
693
|
+
try {
|
|
694
|
+
await e.bitable.appTable.delete({ path: {
|
|
695
|
+
app_token: i.appToken ?? t,
|
|
696
|
+
table_id: i.tableId
|
|
697
|
+
} });
|
|
698
|
+
} catch (e) {
|
|
699
|
+
console.warn(`Warning: Could not delete old table: ${e.message}`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
let a = `fz-task-${r.basename(process.cwd())}`;
|
|
703
|
+
console.log(`Creating table "${a}"...`);
|
|
704
|
+
let o = await e.bitable.appTable.create({
|
|
705
|
+
path: { app_token: t },
|
|
706
|
+
data: { table: {
|
|
707
|
+
name: a,
|
|
708
|
+
fields: B()
|
|
709
|
+
} }
|
|
710
|
+
});
|
|
711
|
+
o.code !== 0 && (console.error(`Failed to create table: ${o.msg} (code: ${o.code})`), process.exit(3));
|
|
712
|
+
let s = o.data.table_id, c = await e.bitable.appTableField.create({
|
|
713
|
+
path: {
|
|
714
|
+
app_token: t,
|
|
715
|
+
table_id: s
|
|
716
|
+
},
|
|
717
|
+
data: {
|
|
718
|
+
field_name: "parent",
|
|
719
|
+
type: z,
|
|
720
|
+
ui_type: "SingleLink",
|
|
721
|
+
property: { table_id: s }
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
c.code !== 0 && console.warn(`Warning: Failed to create parent field: ${c.msg}`), y({ tableId: s }), console.log(`Project initialized! Table ID: ${s}`);
|
|
725
|
+
}
|
|
726
|
+
//#endregion
|
|
727
|
+
//#region src/commands/status.ts
|
|
728
|
+
function W(e) {
|
|
729
|
+
e.command("status").description("Show current config status (global + project)").action(async () => {
|
|
730
|
+
let t = e.opts().profile, n = f();
|
|
731
|
+
if (!n) {
|
|
732
|
+
console.log("Global Auth: Not configured. Run `fz-task auth init`"), console.log("App Token: —"), console.log("Project: —");
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
let r;
|
|
736
|
+
try {
|
|
737
|
+
r = p(n, t);
|
|
738
|
+
} catch (e) {
|
|
739
|
+
console.log(`Global Auth: Error — ${e.message}`), console.log("App Token: —"), console.log("Project: —");
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
let i = r.auth.appId, a = i.length > 10 ? `${i.slice(0, 6)}...${i.slice(-4)}` : i;
|
|
743
|
+
console.log(`Global Auth: OK (App ID: ${a})`);
|
|
744
|
+
let o = v();
|
|
745
|
+
o ? (o.appToken ? console.log(`App Token: ${o.appToken} (project override)`) : console.log(`App Token: ${r.bitable.appToken}`), console.log(`Project: Initialized (Table ID: ${o.tableId})`)) : (console.log(`App Token: ${r.bitable.appToken}`), console.log("Project: Not initialized. Run `fz-task init`"));
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
//#endregion
|
|
595
749
|
//#region src/cli.ts
|
|
596
|
-
var
|
|
597
|
-
|
|
750
|
+
var G = new e();
|
|
751
|
+
G.name("fz-task-cli").description("Feishu Bitable task management CLI").version("0.1.0").option("--profile <name>", "config profile name").option("--format <format>", "output format (json|table)", "table"), x(G), V(G), W(G), N(G), P(G), F(G), G.parse();
|
|
598
752
|
//#endregion
|