botrun-horse 1.0.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.
Files changed (64) hide show
  1. package/README.md +1 -0
  2. package/bin/bh.mjs +193 -0
  3. package/bin/commands/dag-cmd.mjs +74 -0
  4. package/bin/commands/db-cmd.mjs +73 -0
  5. package/bin/commands/doc.mjs +185 -0
  6. package/bin/commands/gemini.mjs +120 -0
  7. package/bin/commands/help.mjs +109 -0
  8. package/bin/commands/legal.mjs +174 -0
  9. package/bin/commands/nchc.mjs +212 -0
  10. package/bin/commands/openrouter.mjs +154 -0
  11. package/bin/commands/prompt.mjs +175 -0
  12. package/bin/commands/schema.mjs +258 -0
  13. package/bin/commands/search.mjs +46 -0
  14. package/bin/commands/writing.mjs +33 -0
  15. package/lib/core/adapters/base.mjs +52 -0
  16. package/lib/core/adapters/claude.mjs +13 -0
  17. package/lib/core/adapters/gemini-api.mjs +174 -0
  18. package/lib/core/adapters/gemini-shared.mjs +164 -0
  19. package/lib/core/adapters/gemini-vertex.mjs +232 -0
  20. package/lib/core/adapters/local.mjs +13 -0
  21. package/lib/core/adapters/nchc.mjs +236 -0
  22. package/lib/core/adapters/openai-shared.mjs +34 -0
  23. package/lib/core/adapters/openrouter.mjs +304 -0
  24. package/lib/core/ai-cache.mjs +277 -0
  25. package/lib/core/ai-router.mjs +217 -0
  26. package/lib/core/cli-utils.mjs +170 -0
  27. package/lib/core/dag.mjs +114 -0
  28. package/lib/core/db.mjs +412 -0
  29. package/lib/core/env.mjs +64 -0
  30. package/lib/core/llm.mjs +58 -0
  31. package/lib/core/paths.mjs +115 -0
  32. package/lib/core/proxy.mjs +46 -0
  33. package/lib/core/watermelon.mjs +9 -0
  34. package/lib/doc/index.mjs +419 -0
  35. package/lib/doc/office2text.mjs +234 -0
  36. package/lib/doc/pdf2text.mjs +133 -0
  37. package/lib/doc/split.mjs +132 -0
  38. package/lib/flows/draft-writing.mjs +29 -0
  39. package/lib/flows/gemini-ask.mjs +185 -0
  40. package/lib/flows/hatch-portal.mjs +13 -0
  41. package/lib/flows/legal-ask.mjs +325 -0
  42. package/lib/flows/openai-agent.mjs +167 -0
  43. package/lib/flows/opencode-agent.mjs +240 -0
  44. package/lib/flows/openrouter-ask.mjs +111 -0
  45. package/lib/flows/review-doc.mjs +18 -0
  46. package/lib/ocr/index.mjs +6 -0
  47. package/lib/portal/hatch.mjs +6 -0
  48. package/lib/portal/index.mjs +6 -0
  49. package/lib/prompt/prompt-search.mjs +55 -0
  50. package/lib/prompt/prompt-store.mjs +94 -0
  51. package/lib/prompt/prompts/zero-framework/coding.md +15 -0
  52. package/lib/prompt/prompts/zero-framework/search.md +12 -0
  53. package/lib/prompt/prompts/zero-framework/slice.md +11 -0
  54. package/lib/search/crawler.mjs +6 -0
  55. package/lib/search/index.mjs +7 -0
  56. package/lib/tools/fs-tools.mjs +268 -0
  57. package/lib/tools/index.mjs +27 -0
  58. package/lib/writing/generate.mjs +86 -0
  59. package/lib/writing/generators/nstc-generators.mjs +279 -0
  60. package/lib/writing/generators/nstc-top5.mjs +554 -0
  61. package/lib/writing/index.mjs +5 -0
  62. package/lib/writing/layouts/nstc-layout.mjs +249 -0
  63. package/lib/writing/renderer.mjs +61 -0
  64. package/package.json +35 -0
@@ -0,0 +1,554 @@
1
+ // lib/writing/generators/nstc-top5.mjs — Top 5 高擬真多頁公文生成器
2
+ // 每個生成器產出 3-6 頁的真實國科會公文
3
+
4
+ import {
5
+ pick, randInt, padZero, genDocNum, genDate, genBudget,
6
+ SCHOOLS, PIS, DEPTS, TOPICS, VENDORS, CHIEFS, MEETING_PLACES,
7
+ } from '../../../projects/nstc/data/fake-data.mjs';
8
+ import {
9
+ drawHeader, drawAttachmentHeader, drawField, drawSection,
10
+ drawNumberedText, drawSubNumberedText, drawParagraph, drawSignature,
11
+ checkPage, drawTable, drawComparisonTable,
12
+ } from '../layouts/nstc-layout.mjs';
13
+
14
+ // =====================================================================
15
+ // #1 — 專題研究計畫補助核定通知(函) 3~4 頁
16
+ // 本文 + 附件:經費核定清單(含多筆計畫的表格)
17
+ // =====================================================================
18
+ export function generate_top5_核定通知(doc, meta) {
19
+ const docNum = genDocNum('函', meta.id);
20
+ const date = genDate();
21
+ const school = pick(SCHOOLS);
22
+ const dept = pick(DEPTS);
23
+
24
+ // ---- 第 1 頁:公文本文 ----
25
+ drawHeader(doc, '國家科學及技術委員會 函');
26
+ let y = 160;
27
+
28
+ y = drawField(doc, '受文者:', school, y);
29
+ y = drawField(doc, '發文日期:', date, y);
30
+ y = drawField(doc, '發文字號:', docNum, y);
31
+ y = drawField(doc, '速別:', '最速件', y);
32
+ y = drawField(doc, '密等及解密條件或保密期限:', '普通', y);
33
+ y = drawField(doc, '附件:', '如說明四(計畫經費核定清單乙份)', y);
34
+ y += 8;
35
+
36
+ y = drawField(doc, '主旨:', `有關貴校教師申請本會114年度專題研究計畫,業經本會審查完竣,核定補助案計${randInt(15, 45)}件,復如說明,請查照。`, y, true);
37
+ y += 5;
38
+
39
+ y = drawSection(doc, '說明:', y);
40
+ y = drawNumberedText(doc, '一、', `依據本會${dept}114年度專題研究計畫審查作業結果辦理。`, y);
41
+ y = drawNumberedText(doc, '二、', '旨揭計畫業經本會各學門召集人及複審委員審查完竣,核定結果詳如附件「計畫經費核定清單」。各計畫執行期限、補助經費及核定項目均載明於核定清單內,請依核定內容辦理。', y);
42
+ y = drawNumberedText(doc, '三、', '補助經費之核撥及支用,應依「國家科學及技術委員會補助專題研究計畫經費處理原則」辦理,重點事項如下:', y);
43
+ y = drawSubNumberedText(doc, '(一)', '多年期計畫每年分二期撥款,第一期款於計畫核定後撥付,第二期款於計畫主持人繳交期中進度報告經審查後撥付。', y);
44
+ y = drawSubNumberedText(doc, '(二)', '請款時應檢具領款收據、補助合約書及請款明細表。', y);
45
+ y = drawSubNumberedText(doc, '(三)', '計畫經費應於執行期間內支用,同一補助項目內支出用途得逕依機構內部程序變更。', y);
46
+ y = drawSubNumberedText(doc, '(四)', '國外差旅費累計流出或流入超過計畫全程該項目原核定金額百分之五十者,執行機構須敘明理由報經本會同意。', y);
47
+ y = drawSubNumberedText(doc, '(五)', '管理費不得自其他項目流入,不同計畫間不得互相流用。', y);
48
+
49
+ y = drawNumberedText(doc, '四、', '檢附計畫經費核定清單乙份,請轉知各計畫主持人知照,並請依規定辦理簽約及請款事宜。', y);
50
+ y = drawNumberedText(doc, '五、', '計畫主持人應依核定計畫內容確實執行,如需變更計畫主持人、經費用途或執行期間,應依「國家科學及技術委員會補助專題研究計畫作業要點」第十七點規定報經本會同意。', y);
51
+ y = drawNumberedText(doc, '六、', '計畫執行期間屆滿後三個月內,計畫主持人應繳交專題研究計畫收支明細報告表及研究成果報告,結餘經費應依規定繳回。', y);
52
+ y = drawNumberedText(doc, '七、', '研究成果如有涉及智慧財產權者,依「科技研究發展成果歸屬及運用辦法」相關規定辦理。', y);
53
+ y += 12;
54
+
55
+ y = drawField(doc, '正本:', school, y);
56
+ y = drawField(doc, '副本:', `${school}研究發展處、${school}主計室、${dept}`, y);
57
+ y += 25;
58
+ drawSignature(doc, y);
59
+
60
+ // ---- 第 2~4 頁:附件 — 經費核定清單表格 ----
61
+ doc.addPage();
62
+ y = drawAttachmentHeader(doc, '國家科學及技術委員會補助專題研究計畫經費核定清單');
63
+
64
+ doc.font('Song').fontSize(10);
65
+ doc.text(`受補助單位:${school}`, 60, y);
66
+ doc.text(`核定日期:${date}`, 350, y);
67
+ y += 18;
68
+ doc.text(`學年度:114年度`, 60, y);
69
+ doc.text(`單位:新臺幣元`, 400, y);
70
+ y += 20;
71
+
72
+ // 產生多筆計畫
73
+ const planCount = randInt(12, 25);
74
+ const headers = ['序號', '計畫編號', '主持人', '計畫名稱', '業務費', '設備費', '國外差旅費', '管理費', '合計'];
75
+ const colWidths = [28, 78, 38, 95, 52, 48, 52, 45, 52];
76
+ const rows = [];
77
+ let totalBiz = 0, totalEquip = 0, totalTravel = 0, totalMgmt = 0, totalAll = 0;
78
+
79
+ for (let i = 0; i < planCount; i++) {
80
+ const projCode = `NSTC ${114}-${randInt(2100, 2900)}-${pick(['E', 'H', 'M', 'B', 'I'])}-${padZero(randInt(1, 200), 3)}-${padZero(randInt(1, 99))}`;
81
+ const piName = pick(PIS);
82
+ const topicShort = pick(TOPICS).substring(0, 18);
83
+ const biz = randInt(200, 1500) * 1000;
84
+ const equip = randInt(0, 3) > 1 ? randInt(50, 500) * 1000 : 0;
85
+ const travel = randInt(0, 3) > 1 ? randInt(30, 200) * 1000 : 0;
86
+ const mgmt = Math.floor(biz * pick([0.10, 0.12, 0.15]));
87
+ const total = biz + equip + travel + mgmt;
88
+ totalBiz += biz; totalEquip += equip; totalTravel += travel; totalMgmt += mgmt; totalAll += total;
89
+
90
+ rows.push([
91
+ String(i + 1),
92
+ projCode,
93
+ piName,
94
+ topicShort,
95
+ biz.toLocaleString(),
96
+ equip ? equip.toLocaleString() : '-',
97
+ travel ? travel.toLocaleString() : '-',
98
+ mgmt.toLocaleString(),
99
+ total.toLocaleString(),
100
+ ]);
101
+ }
102
+
103
+ // 合計列
104
+ rows.push([
105
+ '', '', '', '合 計',
106
+ totalBiz.toLocaleString(),
107
+ totalEquip.toLocaleString(),
108
+ totalTravel.toLocaleString(),
109
+ totalMgmt.toLocaleString(),
110
+ totalAll.toLocaleString(),
111
+ ]);
112
+
113
+ y = drawTable(doc, { headers, colWidths, rows, startY: y, startX: 50, rowHeight: 20, fontSize: 7.5 });
114
+
115
+ y = checkPage(doc, y, 50);
116
+ doc.font('Song').fontSize(9);
117
+ doc.text('備註:', 60, y);
118
+ y += 14;
119
+ doc.text('1. 各計畫經費依核定清單所列金額核撥,不得超支。', 75, y);
120
+ y += 14;
121
+ doc.text('2. 業務費含研究人力費、耗材物品圖書雜項費用、國外學者來臺費用。', 75, y);
122
+ y += 14;
123
+ doc.text('3. 研究設備費係指單價在新臺幣一萬元以上,且使用年限在二年以上之設備。', 75, y);
124
+ y += 14;
125
+ doc.text('4. 管理費由執行機構統籌支用,不得自其他項目流入。', 75, y);
126
+ y += 14;
127
+ doc.text('5. 本清單如有疑義,請逕洽本會各學門承辦人。', 75, y);
128
+ }
129
+
130
+ // =====================================================================
131
+ // #34 — 114年度專題研究計畫申請公告 4~5 頁
132
+ // 完整申請辦法、補助項目、時程表、注意事項
133
+ // =====================================================================
134
+ export function generate_top5_申請公告(doc, meta) {
135
+ const docNum = genDocNum('公告', meta.id);
136
+ const date = genDate();
137
+
138
+ // ---- 第 1 頁 ----
139
+ drawHeader(doc, '國家科學及技術委員會 公告');
140
+ let y = 160;
141
+
142
+ y = drawField(doc, '發文日期:', date, y);
143
+ y = drawField(doc, '發文字號:', docNum, y);
144
+ y = drawField(doc, '附件:', '如公告事項六(申請時程表)', y);
145
+ y += 8;
146
+
147
+ y = drawField(doc, '主旨:', '公告受理114年度(2025年度)專題研究計畫申請案,自即日起開始受理,請查照。', y, true);
148
+ y += 5;
149
+
150
+ y = drawSection(doc, '依據:', y);
151
+ y = drawParagraph(doc, '國家科學及技術委員會補助專題研究計畫作業要點第四點規定。', y);
152
+ y += 3;
153
+
154
+ y = drawSection(doc, '公告事項:', y);
155
+
156
+ y = drawNumberedText(doc, '一、', '計畫類別:', y);
157
+ y = drawSubNumberedText(doc, '(一)', '個別型研究計畫:由單一主持人主導之研究計畫,計畫期限以一年至三年為原則。', y);
158
+ y = drawSubNumberedText(doc, '(二)', '整合型研究計畫:由總計畫主持人整合各子計畫,以跨領域合作方式進行之研究計畫,計畫期限以二年至四年為原則。', y);
159
+ y = drawSubNumberedText(doc, '(三)', '年輕學者養成計畫(含哥倫布計畫及愛因斯坦培植計畫):提供年輕學者較為優渥之研究經費,以利其建立獨立研究能力。', y);
160
+
161
+ y = drawNumberedText(doc, '二、', '申請資格:', y);
162
+ y = drawSubNumberedText(doc, '(一)', '國內公私立大專校院及學術研究機構編制內之專任教學或研究人員。', y);
163
+ y = drawSubNumberedText(doc, '(二)', '中央研究院之研究人員。', y);
164
+ y = drawSubNumberedText(doc, '(三)', '具有博士學位或相當於助理教授以上之研究著作及經歷者。', y);
165
+ y = drawSubNumberedText(doc, '(四)', '年輕學者養成計畫之申請人須符合本會相關規定之年齡限制(取得博士學位未逾七年者)。', y);
166
+
167
+ y = drawNumberedText(doc, '三、', '補助項目及額度:', y);
168
+ y = drawSubNumberedText(doc, '(一)', '業務費:含研究人力費(專、兼任助理、臨時工資等)、耗材物品圖書雜項費用及國外學者來臺費用。個別型計畫每年以新臺幣一百五十萬元為上限(特殊案件得專案報准)。', y);
169
+ y = drawSubNumberedText(doc, '(二)', '研究設備費:係指單價在新臺幣一萬元以上且使用年限在二年以上之設備,應逐項列明名稱、規格、數量及單價。', y);
170
+ y = drawSubNumberedText(doc, '(三)', '國外差旅費:執行國際合作、移地研究或出席國際學術會議之費用,應逐次列明事由、目的地及預估金額。', y);
171
+ y = drawSubNumberedText(doc, '(四)', '管理費:由執行機構配合執行研究計畫所需之費用,按業務費及研究設備費核定金額之一定比例核給。', y);
172
+
173
+ y = drawNumberedText(doc, '四、', '申請方式及應備文件:', y);
174
+ y = drawSubNumberedText(doc, '(一)', '申請人應至本會學術研發服務網(https://wsts.nstc.gov.tw)線上填具申請資料,並上傳研究計畫書全文(PDF格式)。', y);
175
+ y = drawSubNumberedText(doc, '(二)', '申請書應包含下列內容:摘要(中英文各五百字以內)、研究目的與背景、文獻探討、研究方法與步驟、預期成果及貢獻、參考文獻、經費需求及人力配置說明。', y);
176
+ y = drawSubNumberedText(doc, '(三)', '個人資料表應載明近五年內之重要研究成果、發表著作及執行本會研究計畫之情形。', y);
177
+ y = drawSubNumberedText(doc, '(四)', '整合型研究計畫應另附各子計畫間之關聯性及整合機制說明。', y);
178
+ y = drawSubNumberedText(doc, '(五)', '申請機構應依申請人所送資料統一彙送,不受理個人逕送之申請案。', y);
179
+
180
+ y = drawNumberedText(doc, '五、', '審查方式:', y);
181
+ y = drawSubNumberedText(doc, '(一)', '初審:由本會依學門分類,邀請該領域專家學者進行書面審查。每案至少由二位以上審查委員評審。', y);
182
+ y = drawSubNumberedText(doc, '(二)', '複審:由學門召集人及複審委員就初審意見進行綜合評比,決定補助與否及補助額度。', y);
183
+ y = drawSubNumberedText(doc, '(三)', '審查重點包括:研究主題之重要性及創新性、研究方法之可行性與嚴謹度、計畫主持人之研究能力及過往執行成效、經費編列之合理性。', y);
184
+
185
+ y = drawNumberedText(doc, '六、', '申請時程表:', y);
186
+ y += 5;
187
+
188
+ const scheduleHeaders = ['項目', '起始日期', '截止日期', '備註'];
189
+ const scheduleColWidths = [160, 90, 90, 120];
190
+ const scheduleRows = [
191
+ ['線上申請開放', '114年3月1日', '114年5月31日', '逾期系統自動關閉'],
192
+ ['申請機構彙送', '114年3月15日', '114年6月15日', '機構統一送件'],
193
+ ['初審作業', '114年7月1日', '114年9月30日', '書面審查'],
194
+ ['複審作業', '114年10月1日', '114年11月15日', '學門會議'],
195
+ ['核定結果通知', '114年12月1日', '114年12月31日', '公文通知學校'],
196
+ ['第一期款撥付', '115年1月', '115年2月', '簽約後撥款'],
197
+ ];
198
+ y = drawTable(doc, { headers: scheduleHeaders, colWidths: scheduleColWidths, rows: scheduleRows, startY: y, fontSize: 9 });
199
+ y += 5;
200
+
201
+ y = drawNumberedText(doc, '七、', '注意事項:', y);
202
+ y = drawSubNumberedText(doc, '(一)', '同一主持人同一年度以申請一件個別型計畫為限。擔任整合型計畫總主持人者,不得同時申請個別型計畫。', y);
203
+ y = drawSubNumberedText(doc, '(二)', '計畫主持人應確實執行研究計畫,不得將計畫工作委由他人代理或轉包。', y);
204
+ y = drawSubNumberedText(doc, '(三)', '研究計畫涉及人體試驗、基因重組、動物實驗或其他需經倫理審查者,應於計畫書中敘明,並於計畫開始執行前取得相關倫理審查委員會之核准。', y);
205
+ y = drawSubNumberedText(doc, '(四)', '申請人應遵守本會「對研究人員學術倫理規範」,嚴禁造假、變造、抄襲等違反學術倫理之行為。違反者將依「國家科學及技術委員會學術倫理案件處理及審議要點」處理。', y);
206
+ y = drawSubNumberedText(doc, '(五)', '本公告如有未盡事宜,悉依「國家科學及技術委員會補助專題研究計畫作業要點」及相關規定辦理。', y);
207
+
208
+ y = drawNumberedText(doc, '八、', '聯絡方式:', y);
209
+ y = drawSubNumberedText(doc, '', `自然科學及永續研究發展處:(02)2737-7${randInt(100, 999)}`, y);
210
+ y = drawSubNumberedText(doc, '', `工程技術研究發展處:(02)2737-7${randInt(100, 999)}`, y);
211
+ y = drawSubNumberedText(doc, '', `人文及社會科學研究發展處:(02)2737-7${randInt(100, 999)}`, y);
212
+ y = drawSubNumberedText(doc, '', `生命科學研究發展處:(02)2737-7${randInt(100, 999)}`, y);
213
+ y = drawSubNumberedText(doc, '', `科教發展及國際合作處:(02)2737-7${randInt(100, 999)}`, y);
214
+
215
+ y += 20;
216
+ drawSignature(doc, y);
217
+ }
218
+
219
+ // =====================================================================
220
+ // #41 — 委員會議開會通知 3 頁
221
+ // 含完整議程表、出席委員名單
222
+ // =====================================================================
223
+ export function generate_top5_開會通知(doc, meta) {
224
+ const docNum = genDocNum('開會通知單', meta.id);
225
+ const date = genDate();
226
+ const meetMonth = randInt(3, 11);
227
+ const meetDay = randInt(1, 28);
228
+
229
+ // ---- 第 1 頁:開會通知單本文 ----
230
+ drawHeader(doc, '國家科學及技術委員會 開會通知單');
231
+ let y = 160;
232
+
233
+ y = drawField(doc, '發文日期:', date, y);
234
+ y = drawField(doc, '發文字號:', docNum, y);
235
+ y = drawField(doc, '速別:', '速件', y);
236
+ y = drawField(doc, '附件:', '如說明(議程、出席委員名單各乙份)', y);
237
+ y += 12;
238
+
239
+ y = drawField(doc, '會議名稱:', meta.title, y);
240
+ y = drawField(doc, '開會時間:', `中華民國114年${meetMonth}月${meetDay}日(${pick(['星期一', '星期二', '星期三', '星期四', '星期五'])})上午9時30分`, y);
241
+ y = drawField(doc, '開會地點:', '本會3樓國際會議廳(臺北市大安區和平東路二段106號)', y);
242
+ y = drawField(doc, '主持人:', `${pick(CHIEFS)} 主任委員`, y);
243
+ y = drawField(doc, '聯絡人及電話:', `${pick(PIS)}(02-2737-${randInt(7000, 7999)})`, y);
244
+ y = drawField(doc, '傳真號碼:', `(02)2737-${randInt(7800, 7999)}`, y);
245
+ y += 12;
246
+
247
+ y = drawSection(doc, '開會事由:', y);
248
+ y = drawParagraph(doc, '為審議本會114年度重要施政方針、科技發展策略及預算分配等事項,召開本屆第8次委員會議。', y);
249
+ y += 5;
250
+
251
+ y = drawSection(doc, '討論事項:', y);
252
+ y = drawNumberedText(doc, '一、', '確認前次(第7次)會議紀錄。', y);
253
+ y = drawNumberedText(doc, '二、', '報告事項:', y);
254
+ y = drawSubNumberedText(doc, '(一)', '114年度第一季科技預算執行情形報告。', y);
255
+ y = drawSubNumberedText(doc, '(二)', '國際科技合作推動情形報告。', y);
256
+ y = drawSubNumberedText(doc, '(三)', '前瞻基礎建設計畫執行進度報告。', y);
257
+ y = drawNumberedText(doc, '三、', '討論事項:', y);
258
+ y = drawSubNumberedText(doc, '(一)', '115年度科技發展施政計畫(草案)審議。', y);
259
+ y = drawSubNumberedText(doc, '(二)', '人工智慧國家戰略推動方案修正案。', y);
260
+ y = drawSubNumberedText(doc, '(三)', '半導體及量子科技人才培育計畫推動策略。', y);
261
+ y = drawSubNumberedText(doc, '(四)', '淨零碳排科技研發藍圖修訂案。', y);
262
+ y = drawNumberedText(doc, '四、', '臨時動議。', y);
263
+ y += 8;
264
+
265
+ y = drawSection(doc, '備註:', y);
266
+ y = drawNumberedText(doc, '一、', '請委員準時出席。如不克出席,請務必於開會前一日(含)告知聯絡人,並指派代理人出席。代理人出席時應攜帶委任書。', y);
267
+ y = drawNumberedText(doc, '二、', '開會當日請攜帶本通知單至1樓大廳服務台換領臨時識別證入場。', y);
268
+ y = drawNumberedText(doc, '三、', '會議資料將於會前三日以電子郵件傳送各委員,紙本資料於會場領取。', y);
269
+ y = drawNumberedText(doc, '四、', '本會地下一樓設有停車場,請委員多利用大眾運輸工具。', y);
270
+
271
+ y += 20;
272
+ drawSignature(doc, y);
273
+
274
+ // ---- 第 2 頁:議程表 ----
275
+ doc.addPage();
276
+ y = drawAttachmentHeader(doc, `國家科學及技術委員會第12屆第8次委員會議 議程`);
277
+ y += 5;
278
+
279
+ doc.font('Song').fontSize(10);
280
+ doc.text(`日期:中華民國114年${meetMonth}月${meetDay}日(${pick(['星期一', '星期二', '星期三', '星期四', '星期五'])})`, 60, y);
281
+ y += 16;
282
+ doc.text('地點:本會3樓國際會議廳', 60, y);
283
+ y += 20;
284
+
285
+ const agendaHeaders = ['時間', '議程內容', '報告人/主持人'];
286
+ const agendaColWidths = [100, 240, 130];
287
+ const agendaRows = [
288
+ ['09:00-09:30', '報到及領取會議資料', '秘書處'],
289
+ ['09:30-09:40', '主席致詞', `${pick(CHIEFS)} 主任委員`],
290
+ ['09:40-09:50', '確認第7次委員會議紀錄', `${pick(CHIEFS)} 主任委員`],
291
+ ['09:50-10:20', '報告案一:114年度第一季科技預算執行情形', `${pick(DEPTS).substring(0, 6)}報告`],
292
+ ['10:20-10:50', '報告案二:國際科技合作推動情形', '科教發展及國際合作處報告'],
293
+ ['10:50-11:10', '茶敘', ''],
294
+ ['11:10-11:40', '報告案三:前瞻基礎建設計畫執行進度', '前瞻及應用科技處報告'],
295
+ ['11:40-12:20', '討論案一:115年度科技發展施政計畫(草案)', '企劃處提案'],
296
+ ['12:20-13:30', '午餐休息', ''],
297
+ ['13:30-14:10', '討論案二:人工智慧國家戰略推動方案修正案', '前瞻及應用科技處提案'],
298
+ ['14:10-14:50', '討論案三:半導體及量子科技人才培育計畫', '工程技術研究發展處提案'],
299
+ ['14:50-15:10', '茶敘', ''],
300
+ ['15:10-15:50', '討論案四:淨零碳排科技研發藍圖修訂案', '自然科學及永續研究發展處'],
301
+ ['15:50-16:10', '臨時動議', `${pick(CHIEFS)} 主任委員`],
302
+ ['16:10', '散會', ''],
303
+ ];
304
+
305
+ y = drawTable(doc, { headers: agendaHeaders, colWidths: agendaColWidths, rows: agendaRows, startY: y, fontSize: 9, rowHeight: 24 });
306
+
307
+ // ---- 第 3 頁:出席委員名單 ----
308
+ doc.addPage();
309
+ y = drawAttachmentHeader(doc, '出席委員名單');
310
+ y += 5;
311
+
312
+ const memberHeaders = ['序號', '姓名', '現職', '專長領域', '備註'];
313
+ const memberColWidths = [35, 55, 160, 130, 70];
314
+ const memberNames = ['吳政忠', '陳宗權', '林敏聰', '陳炳宇', '張天豪',
315
+ '王瑜', '李遠哲', '廖俊智', '孫璐茜', '張慶瑞', '陳力俊', '彭旭明',
316
+ '劉兆漢', '楊泮池', '周美吟'];
317
+ const positions = [
318
+ '主任委員', '副主任委員', '副主任委員', '委員(國立臺灣大學教授)',
319
+ '委員(國立清華大學教授)', '委員(中央研究院院士)',
320
+ '委員(國立陽明交通大學教授)', '委員(中央研究院院長)',
321
+ '委員(國立成功大學教授)', '委員(國立臺灣大學教授)',
322
+ '委員(國立清華大學教授)', '委員(中央研究院院士)',
323
+ '委員(中央研究院院士)', '委員(國立臺灣大學教授)',
324
+ '委員(中央研究院研究員)',
325
+ ];
326
+ const expertises = [
327
+ '科技政策', '資通訊科技', '材料科學', '電機工程', '機械工程',
328
+ '生命科學', '化學', '生物化學', '物理學', '凝態物理',
329
+ '材料工程', '無機化學', '大氣科學', '醫學', '天文物理',
330
+ ];
331
+ const memberRows = memberNames.map((name, i) => [
332
+ String(i + 1), name, positions[i] || '委員', expertises[i] || '科技政策',
333
+ i < 2 ? '當然委員' : '',
334
+ ]);
335
+
336
+ y = drawTable(doc, { headers: memberHeaders, colWidths: memberColWidths, rows: memberRows, startY: y, fontSize: 9, rowHeight: 22 });
337
+
338
+ y += 10;
339
+ doc.font('Song').fontSize(9);
340
+ doc.text('※ 依國家科學及技術委員會組織法第四條,本會置委員十五人至二十一人。', 60, y);
341
+ y += 14;
342
+ doc.text('※ 如委員不克出席,得指派代理人出席,代理人列席發言但無表決權。', 60, y);
343
+ }
344
+
345
+ // =====================================================================
346
+ // #31 — 補助專題研究計畫作業要點修正令 4~5 頁
347
+ // 含修正條文對照表
348
+ // =====================================================================
349
+ export function generate_top5_修正令(doc, meta) {
350
+ const docNum = genDocNum('令', meta.id);
351
+ const date = genDate();
352
+
353
+ // ---- 第 1 頁:令本文 ----
354
+ drawHeader(doc, '國家科學及技術委員會 令');
355
+ let y = 160;
356
+
357
+ y = drawField(doc, '發文日期:', date, y);
358
+ y = drawField(doc, '發文字號:', docNum, y);
359
+ y = drawField(doc, '附件:', '修正條文對照表乙份', y);
360
+ y += 10;
361
+
362
+ y = drawField(doc, '主旨:', '修正「國家科學及技術委員會補助專題研究計畫作業要點」部分規定,並自即日生效。', y, true);
363
+ y += 5;
364
+
365
+ y = drawSection(doc, '依據:', y);
366
+ y = drawParagraph(doc, '科學技術基本法第六條及國家科學及技術委員會組織法第二條規定。', y);
367
+ y += 3;
368
+
369
+ y = drawSection(doc, '公告事項:', y);
370
+ y = drawNumberedText(doc, '一、', '修正「國家科學及技術委員會補助專題研究計畫作業要點」第三點、第五點、第六點、第十二點、第十五點及第十七點條文。', y);
371
+ y = drawNumberedText(doc, '二、', '本次修正重點如下:', y);
372
+ y = drawSubNumberedText(doc, '(一)', '配合政府推動淨零轉型政策,於第三點新增永續發展相關研究之優先補助規定。', y);
373
+ y = drawSubNumberedText(doc, '(二)', '調整第五點計畫申請期限,將線上申請截止日延長至每年六月三十日。', y);
374
+ y = drawSubNumberedText(doc, '(三)', '修正第六點經費項目分類,增列「國際合作費」為獨立經費項目,以利國際學術合作之推動。', y);
375
+ y = drawSubNumberedText(doc, '(四)', '放寬第十二點計畫執行變更規定,允許計畫主持人於經費百分之二十範圍內自行調整業務費子項配置。', y);
376
+ y = drawSubNumberedText(doc, '(五)', '修正第十五點計畫成果報告繳交期限,延長為計畫期滿後四個月內。', y);
377
+ y = drawSubNumberedText(doc, '(六)', '增訂第十七點之一,明定計畫主持人因故無法繼續執行計畫時之處理程序。', y);
378
+ y = drawNumberedText(doc, '三、', '修正條文對照表如附件。', y);
379
+ y = drawNumberedText(doc, '四、', '本令自即日起生效。各執行機構應依修正後規定辦理,於修正前已核定之計畫得繼續依原核定條件執行。', y);
380
+ y += 20;
381
+ drawSignature(doc, y);
382
+
383
+ // ---- 第 2~4 頁:修正條文對照表 ----
384
+ doc.addPage();
385
+ y = drawAttachmentHeader(doc, '國家科學及技術委員會補助專題研究計畫作業要點修正條文對照表');
386
+ y += 5;
387
+
388
+ const items = [
389
+ {
390
+ article: '第三點',
391
+ newText: '本會補助之專題研究計畫,以具有學術價值、前瞻性或實用性之研究為原則。\n\n前項研究以下列領域為優先補助對象:\n一、基礎科學研究。\n二、應用科技研發。\n三、跨領域整合研究。\n四、永續發展及淨零轉型相關研究。\n五、人工智慧及新興科技研究。',
392
+ oldText: '本會補助之專題研究計畫,以具有學術價值、前瞻性或實用性之研究為原則。\n\n前項研究以下列領域為優先補助對象:\n一、基礎科學研究。\n二、應用科技研發。\n三、跨領域整合研究。',
393
+ },
394
+ {
395
+ article: '第五點',
396
+ newText: '專題研究計畫之申請,應於本會公告受理期間內,由申請機構統一彙送。\n\n前項公告受理期間,線上申請截止日為每年六月三十日,申請機構彙送截止日為每年七月十五日。\n\n申請人應於截止日前完成線上申請程序,逾期不予受理。',
397
+ oldText: '專題研究計畫之申請,應於本會公告受理期間內,由申請機構統一彙送。\n\n前項公告受理期間,線上申請截止日為每年五月三十一日,申請機構彙送截止日為每年六月十五日。\n\n申請人應於截止日前完成線上申請程序,逾期不予受理。',
398
+ },
399
+ {
400
+ article: '第六點',
401
+ newText: '本會補助經費分為下列四類:\n一、業務費:包括研究人力費、耗材物品圖書雜項費用及國外學者來臺費用。\n二、研究設備費:指單價在新臺幣一萬元以上且使用年限在二年以上之設備。\n三、國外差旅費:執行國際合作、移地研究或出席國際學術會議之費用。\n四、國際合作費:與國外研究機構共同執行研究所需之合作經費。',
402
+ oldText: '本會補助經費分為下列三類:\n一、業務費:包括研究人力費、耗材物品圖書雜項費用及國外學者來臺費用。\n二、研究設備費:指單價在新臺幣一萬元以上且使用年限在二年以上之設備。\n三、國外差旅費:執行國際合作、移地研究或出席國際學術會議之費用。',
403
+ },
404
+ {
405
+ article: '第十二點',
406
+ newText: '計畫執行期間如需變更計畫內容,除下列事項得由計畫主持人逕依機構內部程序辦理外,應報經本會同意:\n一、同一補助項目內支出用途之變更。\n二、業務費各子項間百分之二十以內之調整。\n三、增列金額在新臺幣五萬元以下之設備。',
407
+ oldText: '計畫執行期間如需變更計畫內容,除下列事項得由計畫主持人逕依機構內部程序辦理外,應報經本會同意:\n一、同一補助項目內支出用途之變更。\n二、增列金額在新臺幣五萬元以下之設備。',
408
+ },
409
+ {
410
+ article: '第十五點',
411
+ newText: '計畫主持人應於計畫執行期間屆滿後四個月內,繳交研究成果報告及專題研究計畫收支明細報告表。\n\n前項期限届滿未繳交者,本會得暫緩撥付該主持人其他計畫之經費,至繳交後始予撥付。',
412
+ oldText: '計畫主持人應於計畫執行期間屆滿後三個月內,繳交研究成果報告及專題研究計畫收支明細報告表。\n\n前項期限届滿未繳交者,本會得暫緩撥付該主持人其他計畫之經費,至繳交後始予撥付。',
413
+ },
414
+ {
415
+ article: '第十七點之一\n(新增)',
416
+ newText: '計畫主持人因辭職、退休、長期出國、重病或其他不可抗力因素無法繼續執行計畫時,執行機構應即通知本會,並依下列方式處理:\n一、計畫執行未逾二分之一者,得由執行機構推薦合適人選接任,報本會核准。\n二、計畫執行已逾二分之一者,得辦理結案,繳交期末報告及收支明細。\n三、未依前二款辦理者,本會得撤銷補助並追回已撥付經費。',
417
+ oldText: '(本點新增)',
418
+ },
419
+ ];
420
+
421
+ y = drawComparisonTable(doc, { items, startY: y, colWidths: [58, 205, 205] });
422
+ }
423
+
424
+ // =====================================================================
425
+ // #51 — 經費超支核銷簽呈 3~4 頁
426
+ // 含經費收支明細表附件
427
+ // =====================================================================
428
+ export function generate_top5_簽呈(doc, meta) {
429
+ const date = genDate();
430
+ const dept = pick(DEPTS);
431
+ const pi = pick(PIS);
432
+ const school = pick(SCHOOLS);
433
+ const topic = pick(TOPICS);
434
+ const projCode = `NSTC ${114}-${randInt(2100, 2900)}-${pick(['E', 'H', 'M', 'B', 'I'])}-${padZero(randInt(1, 200), 3)}-${padZero(randInt(1, 99))}`;
435
+ const overAmt = randInt(5, 30) * 10000;
436
+
437
+ // ---- 第 1 頁:簽呈本文 ----
438
+ drawHeader(doc, '簽');
439
+ let y = 145;
440
+
441
+ doc.font('SongBold').fontSize(12);
442
+ doc.text(`主管:        會辦:主計室    承辦單位:${dept}`, 60, y);
443
+ y += 35;
444
+
445
+ y = drawField(doc, '簽於:', date, y);
446
+ y += 8;
447
+
448
+ y = drawField(doc, '主旨:', `為${school}${pi}教授執行本會補助專題研究計畫(編號:${projCode})業務費項目超支新臺幣${overAmt.toLocaleString()}元,擬同意由該計畫其他項目流入核銷,簽請核示。`, y, true);
449
+ y += 5;
450
+
451
+ y = drawSection(doc, '說明:', y);
452
+ y = drawNumberedText(doc, '一、', `依據${school}114年${randInt(1, 12)}月${randInt(1, 28)}日校研發字第${randInt(1140000000, 1149999999)}號函辦理。`, y);
453
+ y = drawNumberedText(doc, '二、', `案經查旨揭計畫「${topic}」(計畫編號:${projCode}),執行期間自114年8月1日起至115年7月31日止,核定補助經費總額新臺幣${randInt(80, 200)}萬元。`, y);
454
+ y = drawNumberedText(doc, '三、', '該計畫業務費項下「耗材物品圖書雜項費用」因研究需要增購實驗耗材,致超出原核定金額,經費支用情形詳如附件「經費收支明細表」。', y);
455
+ y = drawNumberedText(doc, '四、', `超支金額新臺幣${overAmt.toLocaleString()}元,擬由下列項目流入補足:`, y);
456
+ y = drawSubNumberedText(doc, '(一)', `研究設備費流入新臺幣${Math.floor(overAmt * 0.6).toLocaleString()}元(該項目尚有結餘)。`, y);
457
+ y = drawSubNumberedText(doc, '(二)', `國外差旅費流入新臺幣${Math.floor(overAmt * 0.4).toLocaleString()}元(原定出國行程取消)。`, y);
458
+ y = drawNumberedText(doc, '五、', '依「國家科學及技術委員會補助專題研究計畫經費處理原則」第三點規定,同一補助項目內支出用途得逕依機構內部程序變更。惟本案涉及跨項目流用,須報經本會同意。', y);
459
+ y = drawNumberedText(doc, '六、', `經查國外差旅費累計流出金額未超過該項目原核定金額百分之五十,符合前開處理原則第三點第二款規定。`, y);
460
+ y = drawNumberedText(doc, '七、', '本案業經本處審核,經費流用尚屬合理,計畫執行進度正常。', y);
461
+ y += 5;
462
+
463
+ y = drawSection(doc, '擬辦:', y);
464
+ y = drawNumberedText(doc, '一、', `擬同意${school}${pi}教授旨揭計畫經費跨項目流用,超支金額新臺幣${overAmt.toLocaleString()}元由研究設備費及國外差旅費流入核銷。`, y);
465
+ y = drawNumberedText(doc, '二、', '奉核後,函復該校據以辦理經費核銷事宜。', y);
466
+ y = drawNumberedText(doc, '三、', '副知本會主計室備查。', y);
467
+ y += 12;
468
+
469
+ doc.font('Song').fontSize(11);
470
+ y = drawField(doc, '承辦人:', pick(PIS), y);
471
+ y = drawField(doc, '科長:', '', y);
472
+ y = drawField(doc, '處長:', '', y);
473
+ y = drawField(doc, '核稿:', '', y);
474
+
475
+ // ---- 第 2~3 頁:附件 — 經費收支明細表 ----
476
+ doc.addPage();
477
+ y = drawAttachmentHeader(doc, '國家科學及技術委員會補助專題研究計畫');
478
+ y += 2;
479
+ doc.font('SongBold').fontSize(14);
480
+ doc.text('經費收支明細報告表', 0, y, { width: 595, align: 'center' });
481
+ y += 25;
482
+
483
+ doc.font('Song').fontSize(10);
484
+ doc.text(`計畫名稱:${topic}`, 60, y);
485
+ y += 16;
486
+ doc.text(`計畫編號:${projCode}`, 60, y);
487
+ doc.text(`主持人:${pi}`, 350, y);
488
+ y += 16;
489
+ doc.text(`執行機構:${school}`, 60, y);
490
+ y += 16;
491
+ doc.text(`執行期間:114年8月1日至115年7月31日`, 60, y);
492
+ doc.text('單位:新臺幣元', 400, y);
493
+ y += 22;
494
+
495
+ const origBiz = randInt(60, 120) * 10000;
496
+ const origEquip = randInt(20, 50) * 10000;
497
+ const origTravel = randInt(15, 35) * 10000;
498
+ const origMgmt = Math.floor(origBiz * 0.12);
499
+ const origTotal = origBiz + origEquip + origTravel + origMgmt;
500
+
501
+ const spentBiz = origBiz + overAmt;
502
+ const spentEquip = origEquip - Math.floor(overAmt * 0.6);
503
+ const spentTravel = origTravel - Math.floor(overAmt * 0.4);
504
+ const spentMgmt = origMgmt;
505
+ const spentTotal = spentBiz + spentEquip + spentTravel + spentMgmt;
506
+
507
+ const detailHeaders = ['經費項目', '核定金額', '流入(+)/流出(-)', '調整後金額', '已支用金額', '結餘金額'];
508
+ const detailColWidths = [100, 72, 72, 72, 72, 72];
509
+
510
+ const detailRows = [
511
+ ['一、業務費', origBiz.toLocaleString(), `+${overAmt.toLocaleString()}`, spentBiz.toLocaleString(), spentBiz.toLocaleString(), '0'],
512
+ [' 1.研究人力費', Math.floor(origBiz * 0.5).toLocaleString(), '-', Math.floor(origBiz * 0.5).toLocaleString(), Math.floor(origBiz * 0.5).toLocaleString(), '0'],
513
+ [' 2.耗材物品圖書雜項', Math.floor(origBiz * 0.45).toLocaleString(), `+${overAmt.toLocaleString()}`, (Math.floor(origBiz * 0.45) + overAmt).toLocaleString(), (Math.floor(origBiz * 0.45) + overAmt).toLocaleString(), '0'],
514
+ [' 3.國外學者來臺', Math.floor(origBiz * 0.05).toLocaleString(), '-', Math.floor(origBiz * 0.05).toLocaleString(), Math.floor(origBiz * 0.05).toLocaleString(), '0'],
515
+ ['二、研究設備費', origEquip.toLocaleString(), `-${Math.floor(overAmt * 0.6).toLocaleString()}`, spentEquip.toLocaleString(), spentEquip.toLocaleString(), '0'],
516
+ ['三、國外差旅費', origTravel.toLocaleString(), `-${Math.floor(overAmt * 0.4).toLocaleString()}`, spentTravel.toLocaleString(), spentTravel.toLocaleString(), '0'],
517
+ ['四、管理費', origMgmt.toLocaleString(), '-', origMgmt.toLocaleString(), origMgmt.toLocaleString(), '0'],
518
+ ['合 計', origTotal.toLocaleString(), '0', spentTotal.toLocaleString(), spentTotal.toLocaleString(), '0'],
519
+ ];
520
+
521
+ y = drawTable(doc, { headers: detailHeaders, colWidths: detailColWidths, rows: detailRows, startY: y, startX: 50, fontSize: 8.5, rowHeight: 22 });
522
+ y += 10;
523
+
524
+ // 備註
525
+ y = checkPage(doc, y, 120);
526
+ doc.font('SongBold').fontSize(10);
527
+ doc.text('備註說明:', 60, y);
528
+ y += 16;
529
+ doc.font('Song').fontSize(9);
530
+ doc.text('1. 業務費超支原因:因研究過程中發現需額外購置高純度化學試劑及精密量測耗材,導致耗材', 75, y);
531
+ y += 14;
532
+ doc.text(' 物品費用超出原預算編列。', 75, y);
533
+ y += 14;
534
+ doc.text('2. 設備費流出說明:原列購置之「高效液相層析儀配件」因廠商報價低於預期,結餘經費流', 75, y);
535
+ y += 14;
536
+ doc.text(' 出補足業務費超支。', 75, y);
537
+ y += 14;
538
+ doc.text('3. 國外差旅費流出說明:原訂赴日本合作研究行程因合作對象時程衝突延至下年度,經費流出。', 75, y);
539
+ y += 14;
540
+ doc.text('4. 本表經費收支均已核實列報,如有不實願負法律責任。', 75, y);
541
+ y += 25;
542
+
543
+ // 簽章欄
544
+ y = checkPage(doc, y, 70);
545
+ doc.font('Song').fontSize(10);
546
+ doc.text('計畫主持人:          會計主管:          機關首長:', 60, y);
547
+ y += 25;
548
+ doc.text(`  ${pi} (簽章)`, 60, y);
549
+ y += 30;
550
+ doc.font('Song').fontSize(8);
551
+ doc.text('※ 本表依「國家科學及技術委員會補助專題研究計畫經費處理原則」第八點規定填具。', 60, y);
552
+ y += 12;
553
+ doc.text('※ 原始支用單據應按補助項目整理審核裝訂成冊,附計畫申請書及經費核定清單,依規定妥善存管備查。', 60, y);
554
+ }
@@ -0,0 +1,5 @@
1
+ // lib/writing/index.mjs — 公文撰寫模組入口
2
+ // 整合 generate.mjs 與 renderer.mjs
3
+
4
+ export { generateAll } from './generate.mjs';
5
+ export { renderPdf } from './renderer.mjs';