@jackwener/opencli 1.5.5 → 1.5.6

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.
Files changed (231) hide show
  1. package/README.md +27 -2
  2. package/README.zh-CN.md +36 -4
  3. package/dist/browser/daemon-client.d.ts +5 -1
  4. package/dist/browser/page.d.ts +6 -0
  5. package/dist/browser/page.js +15 -0
  6. package/dist/cli-manifest.json +1229 -67
  7. package/dist/clis/band/bands.d.ts +1 -0
  8. package/dist/clis/band/bands.js +72 -0
  9. package/dist/clis/band/mentions.d.ts +1 -0
  10. package/dist/clis/band/mentions.js +127 -0
  11. package/dist/clis/band/post.d.ts +1 -0
  12. package/dist/clis/band/post.js +175 -0
  13. package/dist/clis/band/posts.d.ts +1 -0
  14. package/dist/clis/band/posts.js +94 -0
  15. package/dist/clis/doubao/detail.d.ts +1 -0
  16. package/dist/clis/doubao/detail.js +33 -0
  17. package/dist/clis/doubao/detail.test.d.ts +1 -0
  18. package/dist/clis/doubao/detail.test.js +42 -0
  19. package/dist/clis/doubao/history.d.ts +1 -0
  20. package/dist/clis/doubao/history.js +28 -0
  21. package/dist/clis/doubao/history.test.d.ts +1 -0
  22. package/dist/clis/doubao/history.test.js +37 -0
  23. package/dist/clis/doubao/meeting-summary.d.ts +1 -0
  24. package/dist/clis/doubao/meeting-summary.js +39 -0
  25. package/dist/clis/doubao/meeting-transcript.d.ts +1 -0
  26. package/dist/clis/doubao/meeting-transcript.js +36 -0
  27. package/dist/clis/doubao/utils.d.ts +27 -0
  28. package/dist/clis/doubao/utils.js +317 -0
  29. package/dist/clis/doubao/utils.test.d.ts +1 -0
  30. package/dist/clis/doubao/utils.test.js +24 -0
  31. package/dist/clis/douyin/_shared/public-api.d.ts +33 -0
  32. package/dist/clis/douyin/_shared/public-api.js +29 -0
  33. package/dist/clis/douyin/user-videos.d.ts +5 -0
  34. package/dist/clis/douyin/user-videos.js +74 -0
  35. package/dist/clis/douyin/user-videos.test.d.ts +1 -0
  36. package/dist/clis/douyin/user-videos.test.js +108 -0
  37. package/dist/clis/ones/common.d.ts +32 -0
  38. package/dist/clis/ones/common.js +144 -0
  39. package/dist/clis/ones/enrich-tasks.d.ts +5 -0
  40. package/dist/clis/ones/enrich-tasks.js +37 -0
  41. package/dist/clis/ones/login.d.ts +1 -0
  42. package/dist/clis/ones/login.js +80 -0
  43. package/dist/clis/ones/logout.d.ts +1 -0
  44. package/dist/clis/ones/logout.js +17 -0
  45. package/dist/clis/ones/me.d.ts +1 -0
  46. package/dist/clis/ones/me.js +30 -0
  47. package/dist/clis/ones/my-tasks.d.ts +1 -0
  48. package/dist/clis/ones/my-tasks.js +120 -0
  49. package/dist/clis/ones/resolve-labels.d.ts +10 -0
  50. package/dist/clis/ones/resolve-labels.js +64 -0
  51. package/dist/clis/ones/task-helpers.d.ts +29 -0
  52. package/dist/clis/ones/task-helpers.js +212 -0
  53. package/dist/clis/ones/task-helpers.test.d.ts +1 -0
  54. package/dist/clis/ones/task-helpers.test.js +12 -0
  55. package/dist/clis/ones/task.d.ts +1 -0
  56. package/dist/clis/ones/task.js +66 -0
  57. package/dist/clis/ones/tasks.d.ts +1 -0
  58. package/dist/clis/ones/tasks.js +79 -0
  59. package/dist/clis/ones/token-info.d.ts +1 -0
  60. package/dist/clis/ones/token-info.js +42 -0
  61. package/dist/clis/ones/worklog.d.ts +11 -0
  62. package/dist/clis/ones/worklog.js +267 -0
  63. package/dist/clis/ones/worklog.test.d.ts +1 -0
  64. package/dist/clis/ones/worklog.test.js +20 -0
  65. package/dist/clis/spotify/spotify.d.ts +1 -0
  66. package/dist/clis/spotify/spotify.js +316 -0
  67. package/dist/clis/spotify/utils.d.ts +21 -0
  68. package/dist/clis/spotify/utils.js +66 -0
  69. package/dist/clis/spotify/utils.test.d.ts +1 -0
  70. package/dist/clis/spotify/utils.test.js +67 -0
  71. package/dist/clis/tieba/commands.test.d.ts +4 -0
  72. package/dist/clis/tieba/commands.test.js +79 -0
  73. package/dist/clis/tieba/hot.d.ts +1 -0
  74. package/dist/clis/tieba/hot.js +48 -0
  75. package/dist/clis/tieba/posts.d.ts +1 -0
  76. package/dist/clis/tieba/posts.js +85 -0
  77. package/dist/clis/tieba/read.d.ts +1 -0
  78. package/dist/clis/tieba/read.js +140 -0
  79. package/dist/clis/tieba/search.d.ts +1 -0
  80. package/dist/clis/tieba/search.js +108 -0
  81. package/dist/clis/tieba/utils.d.ts +101 -0
  82. package/dist/clis/tieba/utils.js +240 -0
  83. package/dist/clis/tieba/utils.test.d.ts +1 -0
  84. package/dist/clis/tieba/utils.test.js +290 -0
  85. package/dist/clis/weread/book.js +100 -13
  86. package/dist/clis/weread/commands.test.js +221 -0
  87. package/dist/clis/weread/private-api-regression.test.d.ts +1 -0
  88. package/dist/{weread-private-api-regression.test.js → clis/weread/private-api-regression.test.js} +92 -30
  89. package/dist/clis/weread/search-regression.test.d.ts +1 -0
  90. package/dist/clis/weread/search-regression.test.js +407 -0
  91. package/dist/clis/weread/search.js +143 -7
  92. package/dist/clis/weread/shelf.js +13 -95
  93. package/dist/clis/weread/utils.d.ts +46 -0
  94. package/dist/clis/weread/utils.js +214 -7
  95. package/dist/clis/weread/utils.test.js +71 -1
  96. package/dist/clis/xiaohongshu/publish.d.ts +1 -1
  97. package/dist/clis/xiaohongshu/publish.js +78 -31
  98. package/dist/clis/xiaohongshu/publish.test.js +66 -1
  99. package/dist/clis/xiaohongshu/user-helpers.d.ts +1 -0
  100. package/dist/clis/xiaohongshu/user-helpers.js +2 -0
  101. package/dist/clis/xiaohongshu/user-helpers.test.js +18 -0
  102. package/dist/clis/xueqiu/comments.d.ts +118 -0
  103. package/dist/clis/xueqiu/comments.js +354 -0
  104. package/dist/clis/xueqiu/comments.test.d.ts +1 -0
  105. package/dist/clis/xueqiu/comments.test.js +696 -0
  106. package/dist/clis/youtube/transcript.js +2 -4
  107. package/dist/clis/youtube/utils.d.ts +9 -0
  108. package/dist/clis/youtube/utils.js +67 -3
  109. package/dist/clis/youtube/utils.test.d.ts +1 -0
  110. package/dist/clis/youtube/utils.test.js +37 -0
  111. package/dist/clis/youtube/video.js +16 -15
  112. package/dist/clis/zsxq/dynamics.d.ts +1 -0
  113. package/dist/clis/zsxq/dynamics.js +47 -0
  114. package/dist/clis/zsxq/groups.d.ts +1 -0
  115. package/dist/clis/zsxq/groups.js +32 -0
  116. package/dist/clis/zsxq/search.d.ts +1 -0
  117. package/dist/clis/zsxq/search.js +43 -0
  118. package/dist/clis/zsxq/search.test.d.ts +1 -0
  119. package/dist/clis/zsxq/search.test.js +24 -0
  120. package/dist/clis/zsxq/topic.d.ts +1 -0
  121. package/dist/clis/zsxq/topic.js +47 -0
  122. package/dist/clis/zsxq/topic.test.d.ts +1 -0
  123. package/dist/clis/zsxq/topic.test.js +29 -0
  124. package/dist/clis/zsxq/topics.d.ts +1 -0
  125. package/dist/clis/zsxq/topics.js +25 -0
  126. package/dist/clis/zsxq/topics.test.d.ts +1 -0
  127. package/dist/clis/zsxq/topics.test.js +24 -0
  128. package/dist/clis/zsxq/utils.d.ts +97 -0
  129. package/dist/clis/zsxq/utils.js +230 -0
  130. package/dist/commanderAdapter.js +1 -1
  131. package/dist/commanderAdapter.test.js +39 -0
  132. package/dist/external-clis.yaml +17 -0
  133. package/dist/types.d.ts +5 -0
  134. package/docs/.vitepress/config.mts +3 -0
  135. package/docs/adapters/browser/band.md +63 -0
  136. package/docs/adapters/browser/ones.md +59 -0
  137. package/docs/adapters/browser/spotify.md +62 -0
  138. package/docs/adapters/browser/tieba.md +45 -0
  139. package/docs/adapters/browser/xueqiu.md +5 -0
  140. package/docs/adapters/browser/zsxq.md +49 -0
  141. package/docs/adapters/index.md +5 -2
  142. package/docs/adapters-doc/ones.md +32 -0
  143. package/extension/src/background.ts +15 -0
  144. package/extension/src/cdp.ts +42 -0
  145. package/extension/src/protocol.ts +5 -1
  146. package/package.json +1 -1
  147. package/scripts/postinstall.js +16 -0
  148. package/src/browser/daemon-client.ts +5 -1
  149. package/src/browser/page.ts +16 -0
  150. package/src/clis/band/bands.ts +76 -0
  151. package/src/clis/band/mentions.ts +134 -0
  152. package/src/clis/band/post.ts +187 -0
  153. package/src/clis/band/posts.ts +106 -0
  154. package/src/clis/doubao/detail.test.ts +53 -0
  155. package/src/clis/doubao/detail.ts +41 -0
  156. package/src/clis/doubao/history.test.ts +45 -0
  157. package/src/clis/doubao/history.ts +32 -0
  158. package/src/clis/doubao/meeting-summary.ts +53 -0
  159. package/src/clis/doubao/meeting-transcript.ts +48 -0
  160. package/src/clis/doubao/utils.test.ts +45 -0
  161. package/src/clis/doubao/utils.ts +371 -0
  162. package/src/clis/douyin/_shared/public-api.ts +84 -0
  163. package/src/clis/douyin/user-videos.test.ts +122 -0
  164. package/src/clis/douyin/user-videos.ts +101 -0
  165. package/src/clis/ones/common.ts +187 -0
  166. package/src/clis/ones/enrich-tasks.ts +47 -0
  167. package/src/clis/ones/login.ts +103 -0
  168. package/src/clis/ones/logout.ts +19 -0
  169. package/src/clis/ones/me.ts +34 -0
  170. package/src/clis/ones/my-tasks.ts +148 -0
  171. package/src/clis/ones/resolve-labels.ts +80 -0
  172. package/src/clis/ones/task-helpers.test.ts +14 -0
  173. package/src/clis/ones/task-helpers.ts +214 -0
  174. package/src/clis/ones/task.ts +79 -0
  175. package/src/clis/ones/tasks.ts +92 -0
  176. package/src/clis/ones/token-info.ts +46 -0
  177. package/src/clis/ones/worklog.test.ts +24 -0
  178. package/src/clis/ones/worklog.ts +306 -0
  179. package/src/clis/spotify/spotify.ts +328 -0
  180. package/src/clis/spotify/utils.test.ts +87 -0
  181. package/src/clis/spotify/utils.ts +92 -0
  182. package/src/clis/tieba/commands.test.ts +86 -0
  183. package/src/clis/tieba/hot.ts +52 -0
  184. package/src/clis/tieba/posts.ts +108 -0
  185. package/src/clis/tieba/read.ts +158 -0
  186. package/src/clis/tieba/search.ts +119 -0
  187. package/src/clis/tieba/utils.test.ts +322 -0
  188. package/src/clis/tieba/utils.ts +348 -0
  189. package/src/clis/weread/book.ts +116 -13
  190. package/src/clis/weread/commands.test.ts +249 -0
  191. package/src/{weread-private-api-regression.test.ts → clis/weread/private-api-regression.test.ts} +108 -30
  192. package/src/clis/weread/search-regression.test.ts +440 -0
  193. package/src/clis/weread/search.ts +189 -9
  194. package/src/clis/weread/shelf.ts +20 -122
  195. package/src/clis/weread/utils.test.ts +81 -1
  196. package/src/clis/weread/utils.ts +264 -7
  197. package/src/clis/xiaohongshu/publish.test.ts +79 -1
  198. package/src/clis/xiaohongshu/publish.ts +84 -30
  199. package/src/clis/xiaohongshu/user-helpers.test.ts +23 -0
  200. package/src/clis/xiaohongshu/user-helpers.ts +4 -0
  201. package/src/clis/xueqiu/comments.test.ts +823 -0
  202. package/src/clis/xueqiu/comments.ts +461 -0
  203. package/src/clis/youtube/transcript.ts +2 -4
  204. package/src/clis/youtube/utils.test.ts +43 -0
  205. package/src/clis/youtube/utils.ts +69 -0
  206. package/src/clis/youtube/video.ts +16 -15
  207. package/src/clis/zsxq/dynamics.ts +60 -0
  208. package/src/clis/zsxq/groups.ts +41 -0
  209. package/src/clis/zsxq/search.test.ts +29 -0
  210. package/src/clis/zsxq/search.ts +54 -0
  211. package/src/clis/zsxq/topic.test.ts +34 -0
  212. package/src/clis/zsxq/topic.ts +68 -0
  213. package/src/clis/zsxq/topics.test.ts +29 -0
  214. package/src/clis/zsxq/topics.ts +36 -0
  215. package/src/clis/zsxq/utils.ts +351 -0
  216. package/src/commanderAdapter.test.ts +47 -0
  217. package/src/commanderAdapter.ts +1 -1
  218. package/src/external-clis.yaml +17 -0
  219. package/src/types.ts +5 -0
  220. package/tests/e2e/band-auth.test.ts +20 -0
  221. package/tests/e2e/browser-auth-helpers.ts +18 -0
  222. package/tests/e2e/browser-auth.test.ts +35 -47
  223. package/tests/e2e/browser-public.test.ts +288 -0
  224. package/tests/e2e/management.test.ts +1 -1
  225. package/tests/e2e/plugin-management.test.ts +1 -1
  226. package/vitest.config.ts +1 -0
  227. package/SKILL.md +0 -879
  228. package/dist/weread-private-api-regression.test.d.ts +0 -1
  229. package/dist/weread-search-regression.test.d.ts +0 -1
  230. package/dist/weread-search-regression.test.js +0 -39
  231. package/src/weread-search-regression.test.ts +0 -44
@@ -0,0 +1,29 @@
1
+ /**
2
+ * ONES filters/peek 响应解析(tasks / my-tasks 共用)
3
+ */
4
+ export declare function pickTaskTitle(e: Record<string, unknown>): string;
5
+ /** 表格里标题别撑爆终端 */
6
+ export declare function ellipsizeCell(s: string, max?: number): string;
7
+ /** 辅助列:长 uuid 缩略,完整值见 -f json */
8
+ export declare function briefUuid(id: string, head?: number, tail?: number): string;
9
+ export declare function formatStamp(v: unknown): string;
10
+ export declare function flattenPeekGroups(parsed: Record<string, unknown>, limit: number): Record<string, unknown>[];
11
+ /** 工作项状态 uuid(用于查 task_statuses 得中文名) */
12
+ export declare function getTaskStatusRawId(e: Record<string, unknown>): string;
13
+ /** 项目 uuid */
14
+ export declare function getTaskProjectRawId(e: Record<string, unknown>): string;
15
+ /**
16
+ * Project API 里 assess/total/remaining_manhour 多为**定点整数**(与 Web 上「小时」不一致);
17
+ * 常见换算:raw / 1e5 ≈ 小时。若你方实例不同,可设 `ONES_MANHOUR_SCALE`(默认 100000)。
18
+ */
19
+ export declare function onesManhourScale(): number;
20
+ /** 界面/h 小数 → API 内 manhour 整数(与列表「工时」列同一刻度) */
21
+ export declare function hoursToOnesManhourRaw(hours: number): number;
22
+ export declare function formatTaskManhourSummary(e: Record<string, unknown>): string;
23
+ export interface TaskLabelMaps {
24
+ statusByUuid?: Map<string, string>;
25
+ projectByUuid?: Map<string, string>;
26
+ }
27
+ export declare function mapTaskEntry(e: Record<string, unknown>, labels?: TaskLabelMaps): Record<string, string>;
28
+ export declare function defaultPeekBody(query: Record<string, unknown>): Record<string, unknown>;
29
+ export declare function parsePeekLimit(value: unknown, fallback: number): number;
@@ -0,0 +1,212 @@
1
+ /**
2
+ * ONES filters/peek 响应解析(tasks / my-tasks 共用)
3
+ */
4
+ import { CliError } from '../../errors.js';
5
+ /** ONES task 里 field_values 常为 [{ field_uuid, value }, ...] */
6
+ function pickTitleFromFieldValuesArray(fv) {
7
+ if (!Array.isArray(fv))
8
+ return '';
9
+ for (const item of fv) {
10
+ if (!item || typeof item !== 'object')
11
+ continue;
12
+ const row = item;
13
+ const fu = String(row.field_uuid ?? '');
14
+ if (!fu.startsWith('field'))
15
+ continue;
16
+ const v = row.value;
17
+ if (typeof v === 'string' && v.trim())
18
+ return v.trim();
19
+ if (Array.isArray(v) && v.length && typeof v[0] === 'string' && v[0].trim())
20
+ return v[0].trim();
21
+ }
22
+ return '';
23
+ }
24
+ export function pickTaskTitle(e) {
25
+ for (const k of ['summary', 'name', 'title', 'subject']) {
26
+ const v = e[k];
27
+ if (typeof v === 'string' && v.trim())
28
+ return v.trim();
29
+ }
30
+ const fromArr = pickTitleFromFieldValuesArray(e.field_values);
31
+ if (fromArr)
32
+ return fromArr;
33
+ const fv = e.field_values;
34
+ if (fv && typeof fv === 'object' && !Array.isArray(fv)) {
35
+ const o = fv;
36
+ for (const k of ['field001', 'field002', 'field003']) {
37
+ const v = o[k];
38
+ if (typeof v === 'string' && v.trim())
39
+ return v.trim();
40
+ }
41
+ }
42
+ return '';
43
+ }
44
+ /** 表格里标题别撑爆终端 */
45
+ export function ellipsizeCell(s, max = 64) {
46
+ const t = s.trim();
47
+ if (t.length <= max)
48
+ return t;
49
+ return `${t.slice(0, max - 1)}…`;
50
+ }
51
+ /** 辅助列:长 uuid 缩略,完整值见 -f json */
52
+ export function briefUuid(id, head = 6, tail = 4) {
53
+ if (!id)
54
+ return '';
55
+ if (id.length <= head + tail + 1)
56
+ return id;
57
+ return `${id.slice(0, head)}…${id.slice(-tail)}`;
58
+ }
59
+ export function formatStamp(v) {
60
+ if (v == null || v === '')
61
+ return '';
62
+ const n = Number(v);
63
+ if (Number.isNaN(n))
64
+ return String(v);
65
+ const ms = n > 1e14 ? Math.floor(n / 1000) : n > 1e12 ? n : n * 1000;
66
+ try {
67
+ return new Date(ms).toISOString().replace('T', ' ').slice(0, 19);
68
+ }
69
+ catch {
70
+ return String(v);
71
+ }
72
+ }
73
+ export function flattenPeekGroups(parsed, limit) {
74
+ if (!Array.isArray(parsed.groups)) {
75
+ throw new CliError('FETCH_ERROR', 'Unexpected filters/peek response (missing groups)', 'Try -f json; check team UUID and API version.');
76
+ }
77
+ const groups = parsed.groups;
78
+ const rows = [];
79
+ for (const g of groups) {
80
+ const entries = Array.isArray(g.entries) ? g.entries : [];
81
+ for (const e of entries) {
82
+ rows.push(e);
83
+ if (rows.length >= limit)
84
+ break;
85
+ }
86
+ if (rows.length >= limit)
87
+ break;
88
+ }
89
+ return rows.slice(0, limit);
90
+ }
91
+ function fieldArrayFirstString(fv, fieldUuid) {
92
+ if (!Array.isArray(fv))
93
+ return '';
94
+ for (const item of fv) {
95
+ if (!item || typeof item !== 'object')
96
+ continue;
97
+ const row = item;
98
+ if (String(row.field_uuid ?? '') !== fieldUuid)
99
+ continue;
100
+ const v = row.value;
101
+ if (typeof v === 'string')
102
+ return v;
103
+ if (Array.isArray(v) && v[0] != null)
104
+ return String(v[0]);
105
+ }
106
+ return '';
107
+ }
108
+ function fvRecord(e) {
109
+ const fv = e.field_values;
110
+ return fv && typeof fv === 'object' && !Array.isArray(fv) ? fv : null;
111
+ }
112
+ /** 工作项状态 uuid(用于查 task_statuses 得中文名) */
113
+ export function getTaskStatusRawId(e) {
114
+ const fv = e.field_values;
115
+ const fvObj = fvRecord(e);
116
+ if (typeof e.status_uuid === 'string')
117
+ return e.status_uuid;
118
+ return fieldArrayFirstString(fv, 'field016') || (fvObj ? String(fvObj.field016 ?? '') : '');
119
+ }
120
+ /** 项目 uuid */
121
+ export function getTaskProjectRawId(e) {
122
+ const fv = e.field_values;
123
+ const fvObj = fvRecord(e);
124
+ if (typeof e.project_uuid === 'string')
125
+ return e.project_uuid;
126
+ return fieldArrayFirstString(fv, 'field006') || (fvObj ? String(fvObj.field006 ?? '') : '');
127
+ }
128
+ /**
129
+ * Project API 里 assess/total/remaining_manhour 多为**定点整数**(与 Web 上「小时」不一致);
130
+ * 常见换算:raw / 1e5 ≈ 小时。若你方实例不同,可设 `ONES_MANHOUR_SCALE`(默认 100000)。
131
+ */
132
+ export function onesManhourScale() {
133
+ const raw = Number(process.env.ONES_MANHOUR_SCALE?.trim());
134
+ if (Number.isFinite(raw) && raw > 0)
135
+ return raw;
136
+ return 1e5;
137
+ }
138
+ /** 界面/h 小数 → API 内 manhour 整数(与列表「工时」列同一刻度) */
139
+ export function hoursToOnesManhourRaw(hours) {
140
+ if (!Number.isFinite(hours) || hours <= 0)
141
+ return 0;
142
+ return Math.max(1, Math.round(hours * onesManhourScale()));
143
+ }
144
+ function formatHoursShort(hours) {
145
+ if (!Number.isFinite(hours))
146
+ return '';
147
+ const snapped = Math.round(hours * 1e6) / 1e6;
148
+ const near = Math.round(snapped);
149
+ if (Math.abs(snapped - near) < 1e-5)
150
+ return `${near}h`;
151
+ const t = Math.round(snapped * 10) / 10;
152
+ return Number.isInteger(t) ? `${t}h` : `${t.toFixed(1)}h`;
153
+ }
154
+ function formatManhourSegment(label, v) {
155
+ if (v == null || v === '')
156
+ return null;
157
+ const n = Number(v);
158
+ if (!Number.isFinite(n))
159
+ return null;
160
+ const hours = n / onesManhourScale();
161
+ return `${label}${formatHoursShort(hours)}`;
162
+ }
163
+ export function formatTaskManhourSummary(e) {
164
+ const parts = [];
165
+ const a = formatManhourSegment('估', e.assess_manhour);
166
+ const t = formatManhourSegment('登', e.total_manhour);
167
+ const r = formatManhourSegment('余', e.remaining_manhour);
168
+ if (a)
169
+ parts.push(a);
170
+ if (t)
171
+ parts.push(t);
172
+ if (r)
173
+ parts.push(r);
174
+ return parts.length ? parts.join(' ') : '—';
175
+ }
176
+ export function mapTaskEntry(e, labels) {
177
+ const statusId = getTaskStatusRawId(e);
178
+ const projectId = getTaskProjectRawId(e);
179
+ const fullUuid = String(e.uuid ?? '');
180
+ const title = ellipsizeCell(pickTaskTitle(e));
181
+ const briefIfLong = (s) => (s.length > 14 ? briefUuid(s) : s);
182
+ const statusLabel = labels?.statusByUuid?.get(statusId) ?? briefIfLong(statusId);
183
+ const projectLabel = labels?.projectByUuid?.get(projectId) ?? briefIfLong(projectId);
184
+ return {
185
+ title,
186
+ status: ellipsizeCell(statusLabel, 20),
187
+ project: ellipsizeCell(projectLabel, 40),
188
+ uuid: fullUuid,
189
+ updated: formatStamp(e.server_update_stamp),
190
+ 工时: ellipsizeCell(formatTaskManhourSummary(e), 36),
191
+ };
192
+ }
193
+ export function defaultPeekBody(query) {
194
+ return {
195
+ with_boards: false,
196
+ boards: null,
197
+ query,
198
+ group_by: '',
199
+ sort: [{ create_time: { order: 'desc' } }],
200
+ include_subtasks: false,
201
+ include_status_uuid: true,
202
+ include_issue_type: false,
203
+ include_project_uuid: true,
204
+ is_show_derive: false,
205
+ };
206
+ }
207
+ export function parsePeekLimit(value, fallback) {
208
+ const parsed = Number.parseInt(String(value ?? ''), 10);
209
+ if (!Number.isFinite(parsed) || parsed <= 0)
210
+ return fallback;
211
+ return Math.max(1, Math.min(500, parsed));
212
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { parsePeekLimit } from './task-helpers.js';
3
+ describe('parsePeekLimit', () => {
4
+ it('returns the fallback when the input is not numeric', () => {
5
+ expect(parsePeekLimit('abc', 30)).toBe(30);
6
+ });
7
+ it('clamps the input into the supported range', () => {
8
+ expect(parsePeekLimit('0', 30)).toBe(30);
9
+ expect(parsePeekLimit('999', 30)).toBe(500);
10
+ expect(parsePeekLimit('42', 30)).toBe(42);
11
+ });
12
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { CliError } from '../../errors.js';
3
+ import { onesFetchInPage } from './common.js';
4
+ import { formatStamp } from './task-helpers.js';
5
+ /**
6
+ * 工作项详情 — 对应前端路由 …/team/<team>/filter/view/…/task/<uuid>
7
+ * API: GET team/:teamUUID/task/:taskUUIDOrNumber/info
8
+ * @see https://docs.ones.cn/project/open-api-doc/project/task.html
9
+ */
10
+ cli({
11
+ site: 'ones',
12
+ name: 'task',
13
+ description: 'ONES — work item detail (GET team/:team/task/:id/info); id is URL segment after …/task/',
14
+ domain: 'ones.cn',
15
+ strategy: Strategy.COOKIE,
16
+ browser: true,
17
+ navigateBefore: false,
18
+ args: [
19
+ {
20
+ name: 'id',
21
+ type: 'str',
22
+ required: true,
23
+ positional: true,
24
+ help: 'Work item UUID (often 16 chars) from …/task/<id>',
25
+ },
26
+ {
27
+ name: 'team',
28
+ type: 'str',
29
+ required: false,
30
+ help: 'Team UUID (8 chars from …/team/<team>/…), or set ONES_TEAM_UUID',
31
+ },
32
+ ],
33
+ columns: ['uuid', 'summary', 'number', 'status_uuid', 'assign', 'owner', 'project_uuid', 'updated'],
34
+ func: async (page, kwargs) => {
35
+ const id = String(kwargs.id ?? '').trim();
36
+ if (!id) {
37
+ throw new CliError('CONFIG', 'task id required', 'Pass the work item uuid from the URL path …/task/<id>');
38
+ }
39
+ const team = kwargs.team?.trim() ||
40
+ process.env.ONES_TEAM_UUID?.trim() ||
41
+ process.env.ONES_TEAM_ID?.trim();
42
+ if (!team) {
43
+ throw new CliError('CONFIG', 'team UUID required', 'Use --team <teamUUID> or set ONES_TEAM_UUID (from …/team/<team>/…).');
44
+ }
45
+ const path = `team/${team}/task/${encodeURIComponent(id)}/info`;
46
+ const data = (await onesFetchInPage(page, path, { method: 'GET' }));
47
+ if (typeof data.uuid !== 'string') {
48
+ const hint = typeof data.reason === 'string'
49
+ ? data.reason
50
+ : 'Use -f json to inspect response; check id length (often 16) and team.';
51
+ throw new CliError('FETCH_ERROR', `ONES task info: ${hint}`, 'Confirm task uuid and team match the browser URL.');
52
+ }
53
+ return [
54
+ {
55
+ uuid: String(data.uuid),
56
+ summary: String(data.summary ?? ''),
57
+ number: data.number != null ? String(data.number) : '',
58
+ status_uuid: String(data.status_uuid ?? ''),
59
+ assign: String(data.assign ?? ''),
60
+ owner: String(data.owner ?? ''),
61
+ project_uuid: String(data.project_uuid ?? ''),
62
+ updated: formatStamp(data.server_update_stamp),
63
+ },
64
+ ];
65
+ },
66
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { CliError } from '../../errors.js';
3
+ import { gotoOnesHome, onesFetchInPage } from './common.js';
4
+ import { enrichPeekEntriesWithDetails } from './enrich-tasks.js';
5
+ import { resolveTaskListLabels } from './resolve-labels.js';
6
+ import { defaultPeekBody, flattenPeekGroups, mapTaskEntry, parsePeekLimit } from './task-helpers.js';
7
+ function buildQuery(project, assign) {
8
+ const must = [];
9
+ if (project?.trim()) {
10
+ must.push({ in: { 'field_values.field006': [project.trim()] } });
11
+ }
12
+ if (assign?.trim()) {
13
+ must.push({ equal: { assign: assign.trim() } });
14
+ }
15
+ if (must.length === 0) {
16
+ return { must: [] };
17
+ }
18
+ return { must };
19
+ }
20
+ cli({
21
+ site: 'ones',
22
+ name: 'tasks',
23
+ description: 'ONES Project API — list work items (POST team/:team/filters/peek); use token-info -f json for team uuid',
24
+ domain: 'ones.cn',
25
+ strategy: Strategy.COOKIE,
26
+ browser: true,
27
+ navigateBefore: false,
28
+ args: [
29
+ {
30
+ name: 'team',
31
+ type: 'str',
32
+ required: false,
33
+ positional: true,
34
+ help: 'Team UUID (8 chars), or set ONES_TEAM_UUID',
35
+ },
36
+ {
37
+ name: 'project',
38
+ type: 'str',
39
+ required: false,
40
+ help: 'Filter by project UUID (field006 / 所属项目)',
41
+ },
42
+ {
43
+ name: 'assign',
44
+ type: 'str',
45
+ required: false,
46
+ help: 'Filter by assignee user UUID (负责人 assign)',
47
+ },
48
+ {
49
+ name: 'limit',
50
+ type: 'int',
51
+ default: 30,
52
+ help: 'Max rows after flattening groups (default 30)',
53
+ },
54
+ ],
55
+ columns: ['title', 'status', 'project', 'uuid', 'updated', '工时'],
56
+ func: async (page, kwargs) => {
57
+ const team = kwargs.team?.trim() ||
58
+ process.env.ONES_TEAM_UUID?.trim() ||
59
+ process.env.ONES_TEAM_ID?.trim();
60
+ if (!team) {
61
+ throw new CliError('CONFIG', 'team UUID required', 'Pass team as first argument or set ONES_TEAM_UUID (see `opencli ones token-info -f json` → teams[].uuid).');
62
+ }
63
+ const project = kwargs.project?.trim();
64
+ const assign = kwargs.assign?.trim();
65
+ const limit = parsePeekLimit(kwargs.limit, 30);
66
+ await gotoOnesHome(page);
67
+ const body = defaultPeekBody(buildQuery(project, assign));
68
+ const path = `team/${team}/filters/peek`;
69
+ const parsed = (await onesFetchInPage(page, path, {
70
+ method: 'POST',
71
+ body: JSON.stringify(body),
72
+ skipGoto: true,
73
+ }));
74
+ const entries = flattenPeekGroups(parsed, limit);
75
+ const enriched = await enrichPeekEntriesWithDetails(page, team, entries, true);
76
+ const labels = await resolveTaskListLabels(page, team, enriched, true);
77
+ return enriched.map((e) => mapTaskEntry(e, labels));
78
+ },
79
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { CliError } from '../../errors.js';
3
+ import { onesFetchInPage } from './common.js';
4
+ cli({
5
+ site: 'ones',
6
+ name: 'token-info',
7
+ description: 'ONES Project API — session detail (GET auth/token_info) via Chrome Bridge: user, teams, org',
8
+ domain: 'ones.cn',
9
+ strategy: Strategy.COOKIE,
10
+ browser: true,
11
+ navigateBefore: false,
12
+ args: [],
13
+ columns: ['uuid', 'name', 'email', 'teams', 'org_name'],
14
+ func: async (page) => {
15
+ const root = (await onesFetchInPage(page, 'auth/token_info'));
16
+ const user = root.user && typeof root.user === 'object' ? root.user : null;
17
+ if (!user?.uuid) {
18
+ throw new CliError('FETCH_ERROR', 'Unexpected auth/token_info response', 'Try `opencli ones me -f json` or check ONES_* env vars.');
19
+ }
20
+ const teamRows = Array.isArray(root.teams) ? root.teams : [];
21
+ const teamsHint = teamRows
22
+ .map((t) => {
23
+ const n = String(t.name ?? '').trim();
24
+ const u = String(t.uuid ?? '').trim();
25
+ if (n && u)
26
+ return `${n} (${u})`;
27
+ return u || n;
28
+ })
29
+ .filter(Boolean)
30
+ .join(', ');
31
+ const org = root.org && typeof root.org === 'object' ? root.org : null;
32
+ return [
33
+ {
34
+ uuid: String(user.uuid),
35
+ name: String(user.name ?? ''),
36
+ email: String(user.email ?? ''),
37
+ teams: teamsHint,
38
+ org_name: org ? String(org.name ?? '') : '',
39
+ },
40
+ ];
41
+ },
42
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Log/backfill work hours. Project API paths vary by deployment,
3
+ * so we try common endpoints in sequence.
4
+ */
5
+ export declare function buildAddManhourGraphqlBody(input: {
6
+ ownerId: string;
7
+ taskId: string;
8
+ startTime: number;
9
+ rawManhour: number;
10
+ note: string;
11
+ }): string;