oasis_test_v2 2.2.120 → 2.2.122
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/index.js +600 -158
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1569,6 +1569,21 @@ function todoAssignees(todo) {
|
|
|
1569
1569
|
function isTodoParty(todo, actorId) {
|
|
1570
1570
|
return todo.creatorActorId === actorId || todoAssignees(todo).includes(actorId);
|
|
1571
1571
|
}
|
|
1572
|
+
function isTodoDelegated(todo) {
|
|
1573
|
+
return !todoAssignees(todo).includes(todo.creatorActorId);
|
|
1574
|
+
}
|
|
1575
|
+
function isTodoUnsent(todo) {
|
|
1576
|
+
return isTodoDelegated(todo) && !todo.sentAt;
|
|
1577
|
+
}
|
|
1578
|
+
function canReadTodo(todo, actorId) {
|
|
1579
|
+
return !isTodoUnsent(todo) || todo.creatorActorId === actorId;
|
|
1580
|
+
}
|
|
1581
|
+
function canWriteTodo(todo, actorId) {
|
|
1582
|
+
return isTodoUnsent(todo) ? todo.creatorActorId === actorId : isTodoParty(todo, actorId);
|
|
1583
|
+
}
|
|
1584
|
+
function isTodoAssignedToByOther(todo, actorId) {
|
|
1585
|
+
return isTodoDelegated(todo) && todoAssignees(todo).includes(actorId);
|
|
1586
|
+
}
|
|
1572
1587
|
function isValidTodoTime(time4) {
|
|
1573
1588
|
const m4 = /^(\d{2}):(\d{2})$/.exec(time4);
|
|
1574
1589
|
return !!m4 && Number(m4[1]) <= 23 && Number(m4[2]) <= 59;
|
|
@@ -1609,11 +1624,12 @@ function compareTodos(a5, b6) {
|
|
|
1609
1624
|
if (a5.createdAt !== b6.createdAt) return a5.createdAt < b6.createdAt ? -1 : 1;
|
|
1610
1625
|
return a5.id < b6.id ? -1 : a5.id > b6.id ? 1 : 0;
|
|
1611
1626
|
}
|
|
1612
|
-
var WEEKDAY_LABEL;
|
|
1627
|
+
var TODO_STATUSES, WEEKDAY_LABEL;
|
|
1613
1628
|
var init_todos = __esm({
|
|
1614
1629
|
"../contract/src/todos.ts"() {
|
|
1615
1630
|
"use strict";
|
|
1616
1631
|
init_time_zone();
|
|
1632
|
+
TODO_STATUSES = ["draft", "open", "done", "cancelled"];
|
|
1617
1633
|
WEEKDAY_LABEL = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
1618
1634
|
}
|
|
1619
1635
|
});
|
|
@@ -180598,9 +180614,9 @@ var init_live_chat = __esm({
|
|
|
180598
180614
|
* `chat_messages` 不补:二期页面只从 `chat_items` 出历史,agent 也已经读到,不需要再捎带。
|
|
180599
180615
|
*/
|
|
180600
180616
|
recordLateConsumedInput(turn, text7, opts, ord) {
|
|
180601
|
-
const
|
|
180617
|
+
const shown2 = opts?.displayText ?? text7;
|
|
180602
180618
|
try {
|
|
180603
|
-
return turn.items?.recordUserInput(
|
|
180619
|
+
return turn.items?.recordUserInput(shown2, opts?.attachments, opts?.clientKeys, {
|
|
180604
180620
|
...ord !== void 0 ? { ord } : {},
|
|
180605
180621
|
// ADR 0615:认领那个乐观气泡(问出「读到了」的这一档,气泡此前没被撤掉,正好就地转成真行)。
|
|
180606
180622
|
...opts?.submittedClientKey ? { clientSubmitId: opts.submittedClientKey } : {}
|
|
@@ -180610,10 +180626,10 @@ var init_live_chat = __esm({
|
|
|
180610
180626
|
}
|
|
180611
180627
|
}
|
|
180612
180628
|
commitUserInput(turn, text7, opts) {
|
|
180613
|
-
const
|
|
180629
|
+
const shown2 = opts?.displayText ?? text7;
|
|
180614
180630
|
const userPart = {
|
|
180615
180631
|
type: "user",
|
|
180616
|
-
text:
|
|
180632
|
+
text: shown2,
|
|
180617
180633
|
...opts?.attachments?.length ? { attachments: opts.attachments } : {}
|
|
180618
180634
|
};
|
|
180619
180635
|
const segmentEnd = (() => {
|
|
@@ -180637,7 +180653,7 @@ var init_live_chat = __esm({
|
|
|
180637
180653
|
let userItemId;
|
|
180638
180654
|
const adopt = opts?.submittedClientKey ? { clientSubmitId: opts.submittedClientKey } : void 0;
|
|
180639
180655
|
try {
|
|
180640
|
-
userItemId = turn.items?.recordUserInput(
|
|
180656
|
+
userItemId = turn.items?.recordUserInput(shown2, opts?.attachments, opts?.clientKeys, adopt);
|
|
180641
180657
|
} catch {
|
|
180642
180658
|
}
|
|
180643
180659
|
const withSeq = { ...userPart, seq: ++turn.seq };
|
|
@@ -180664,12 +180680,12 @@ var init_live_chat = __esm({
|
|
|
180664
180680
|
operation: "completed",
|
|
180665
180681
|
payload: {
|
|
180666
180682
|
role: "user",
|
|
180667
|
-
text:
|
|
180683
|
+
text: shown2,
|
|
180668
180684
|
...liveAttachments?.length ? { attachments: liveAttachments } : {}
|
|
180669
180685
|
}
|
|
180670
180686
|
});
|
|
180671
180687
|
try {
|
|
180672
|
-
turn.onUserInput?.(
|
|
180688
|
+
turn.onUserInput?.(shown2, opts?.attachments);
|
|
180673
180689
|
} catch {
|
|
180674
180690
|
}
|
|
180675
180691
|
return userItemId;
|
|
@@ -184464,7 +184480,7 @@ function buildEscalationChatContext(input) {
|
|
|
184464
184480
|
manual: "\u4EBA\u5DE5\u4E0A\u62A5\uFF08\u6709\u4EBA\u7528 oasis escalate \u63D0\u7684\uFF09"
|
|
184465
184481
|
};
|
|
184466
184482
|
const others = snap.issues.filter((row) => row.kind === "escalation" && !row.resolvedAt && row.id !== issue2.id).sort((a5, b6) => String(a5.createdAt).localeCompare(String(b6.createdAt)));
|
|
184467
|
-
const
|
|
184483
|
+
const shown2 = others.slice(0, OTHER_OPEN_LIMIT);
|
|
184468
184484
|
const lines = [
|
|
184469
184485
|
"\u3010\u8FD9\u6761\u4F1A\u8BDD\u662F\u4EBA\u4ECE\u4E00\u6761\u4E0A\u62A5\uFF08\u5347\u7EA7\u5361\uFF09\u70B9\u8FDB\u6765\u7684\u2014\u2014\u4F60\u662F\u8FD9\u5F20\u4EFB\u52A1\u7684\u534F\u8C03\u8005\uFF0C\u4EBA\u662F\u6765\u627E\u4F60\u4E00\u8D77\u5904\u7F6E\u8FD9\u6761\u4E0A\u62A5\u7684\u3002",
|
|
184470
184486
|
"\u4EE5\u4E0B\u662F\u8FD9\u6761\u4E0A\u62A5\u6B64\u523B\u7684\u4E8B\u5B9E\uFF0C\u4E0E\u4EBA\u5728\u754C\u9762\u4E0A\u770B\u5230\u7684\u90A3\u6BB5\u5F00\u573A\u767D\u540C\u6E90\u3002\u522B\u53CD\u95EE\u300C\u4F60\u8BF4\u7684\u662F\u54EA\u4EF6\u4E8B\u300D\uFF1B\u8981\u66F4\u7EC6\u7684\u73B0\u72B6\u81EA\u5DF1\u7528 `oasis status` / `oasis content <id>` \u73B0\u67E5\u56FE\u3002\u3011",
|
|
@@ -184489,8 +184505,8 @@ function buildEscalationChatContext(input) {
|
|
|
184489
184505
|
lines.push(
|
|
184490
184506
|
"",
|
|
184491
184507
|
others.length ? `\u8FD9\u5F20\u4EFB\u52A1\u4E0A\u8FD8\u5F00\u7740\u53E6\u5916 ${others.length} \u6761\u4E0A\u62A5\uFF1A` : "\u8FD9\u5F20\u4EFB\u52A1\u4E0A\u6CA1\u6709\u522B\u7684\u672A\u5173\u4E0A\u62A5\u3002",
|
|
184492
|
-
...
|
|
184493
|
-
...others.length >
|
|
184508
|
+
...shown2.map((row) => ` \xB7 ${row.id}${row.aboutNodeId ? `\uFF08\u8282\u70B9 ${row.aboutNodeId}\uFF09` : ""}\uFF1A${oneLine(row.body)}`),
|
|
184509
|
+
...others.length > shown2.length ? [` \xB7 \uFF08\u53E6\u6709 ${others.length - shown2.length} \u6761\u6CA1\u5217\u51FA\u6765\uFF0C\u7528 \`oasis status\` \u770B\u5168\uFF09`] : [],
|
|
184494
184510
|
"",
|
|
184495
184511
|
'\u5904\u7F6E\u5B8C\u8FD9\u6761\u4E0A\u62A5\uFF0C\u5728\u672C\u4F1A\u8BDD\u91CC\u8DD1 `oasis resolve-escalation --reason "<\u7ED3\u8BBA>"` \u5373\u53EF\u2014\u2014\u4F1A\u8BDD\u5DF2\u7ECF\u7ED1\u7740\u8FD9\u6761\u4E0A\u62A5\uFF0C\u4E0D\u7528\u5E26 id\uFF08\u5B83\u4F1A\u8F6C\u6210\u4E00\u5F20\u5F85\u4EBA\u786E\u8BA4\u7684\u5361\uFF0C\u4E0D\u662F\u7ACB\u523B\u751F\u6548\uFF09\u3002',
|
|
184496
184512
|
...kind === "output_stall" && node2 ? [`\u96F6\u4EA7\u51FA\u6B62\u635F\u540C\u65F6\u6682\u505C\u4E86\u6D3E\u53D1\uFF0C\u786E\u8BA4\u6839\u56E0\u5904\u7406\u6389\u4E4B\u540E\u8FD8\u8981 \`oasis release ${node2.id}\` \u624D\u4F1A\u91CD\u65B0\u6D3E\u3002`] : [],
|
|
@@ -214604,13 +214620,13 @@ async function startOasisServer(opts) {
|
|
|
214604
214620
|
...clientAttached ? { clientAttached } : {}
|
|
214605
214621
|
}, clientKey, attachments, retainLocallyOnFailure, resend ? { resend: true } : void 0);
|
|
214606
214622
|
if ((r5.insertStatus === "uncertain" || r5.orphanedReturn) && clientKey) {
|
|
214607
|
-
const
|
|
214623
|
+
const shown2 = r5.batch.map((m4) => m4.text).filter(Boolean).join("\n\n") || text7;
|
|
214608
214624
|
await landUndeliveredInsert({
|
|
214609
214625
|
store,
|
|
214610
214626
|
itemStore,
|
|
214611
214627
|
chatSessionId: sid,
|
|
214612
214628
|
clientKey,
|
|
214613
|
-
text:
|
|
214629
|
+
text: shown2,
|
|
214614
214630
|
attachments: attachments ?? [],
|
|
214615
214631
|
note: r5.orphanedReturn ? INSERT_RETURNED_NOTE : INSERT_UNCERTAIN_NOTE
|
|
214616
214632
|
});
|
|
@@ -244306,6 +244322,10 @@ var init_notification_store = __esm({
|
|
|
244306
244322
|
this.save();
|
|
244307
244323
|
return row;
|
|
244308
244324
|
}
|
|
244325
|
+
async findBySource(recipient, sourceKey) {
|
|
244326
|
+
const hits = [...this.rows.values()].filter((r5) => r5.recipient === recipient && r5.sourceKey === sourceKey).sort(newerFirst);
|
|
244327
|
+
return hits[0] ?? null;
|
|
244328
|
+
}
|
|
244309
244329
|
async listPending() {
|
|
244310
244330
|
return [...this.rows.values()].filter((r5) => r5.status === "pending");
|
|
244311
244331
|
}
|
|
@@ -244470,6 +244490,13 @@ var init_notification_store = __esm({
|
|
|
244470
244490
|
if (!e5.rows[0]) throw new Error(`notification insert conflicted but no live row: ${n3.recipient} ${n3.sourceKey}`);
|
|
244471
244491
|
return fromRow(e5.rows[0]);
|
|
244472
244492
|
}
|
|
244493
|
+
async findBySource(recipient, sourceKey) {
|
|
244494
|
+
const r5 = await this.sql.query(
|
|
244495
|
+
`SELECT * FROM ${this.t} WHERE recipient = $1 AND source_key = $2 ORDER BY entered_at DESC, id DESC LIMIT 1`,
|
|
244496
|
+
[recipient, sourceKey]
|
|
244497
|
+
);
|
|
244498
|
+
return r5.rows[0] ? fromRow(r5.rows[0]) : null;
|
|
244499
|
+
}
|
|
244473
244500
|
async listPending() {
|
|
244474
244501
|
const r5 = await this.sql.query(`SELECT * FROM ${this.t} WHERE status = 'pending'`);
|
|
244475
244502
|
return r5.rows.map(fromRow);
|
|
@@ -253045,10 +253072,10 @@ function mergedPushPayload(first, queued, companyId) {
|
|
|
253045
253072
|
const all2 = [first, ...queued];
|
|
253046
253073
|
const latest = all2[all2.length - 1];
|
|
253047
253074
|
const others = all2.slice(0, -1).map((r5) => r5.title.trim()).filter(Boolean);
|
|
253048
|
-
const
|
|
253075
|
+
const shown2 = others.slice(-OTHERS_LIMIT);
|
|
253049
253076
|
const task = latest.workspaceTitle?.trim() || first.workspaceTitle?.trim() || OASIS;
|
|
253050
253077
|
const lines = [latest.title.trim() || OASIS];
|
|
253051
|
-
if (
|
|
253078
|
+
if (shown2.length > 0) lines.push(`\u53E6\u6709\uFF1A${shown2.join("\u3001")}${others.length > shown2.length ? " \u7B49" : ""}`);
|
|
253052
253079
|
return {
|
|
253053
253080
|
title: `${task} \xB7 ${all2.length} \u6761\u5F85\u5904\u7406`,
|
|
253054
253081
|
body: lines.join("\n"),
|
|
@@ -257647,14 +257674,57 @@ var init_flag = __esm({
|
|
|
257647
257674
|
}
|
|
257648
257675
|
});
|
|
257649
257676
|
|
|
257650
|
-
// ../server/src/domains/todos/
|
|
257651
|
-
function
|
|
257652
|
-
if (
|
|
257653
|
-
|
|
257677
|
+
// ../server/src/domains/todos/notices.ts
|
|
257678
|
+
function shown(field, v2, nameOf2) {
|
|
257679
|
+
if (v2 === void 0 || v2 === "") return "\uFF08\u7A7A\uFF09";
|
|
257680
|
+
if (field === "dueDate" || field === "startDate") return formatTodoDue(v2);
|
|
257681
|
+
if (field === "assignee") return nameOf2(v2);
|
|
257682
|
+
return clip4(v2);
|
|
257683
|
+
}
|
|
257684
|
+
function assignedTitle(who, todo) {
|
|
257685
|
+
return `${who} \u6D3E\u7ED9\u4F60\uFF1A${todo.title}\uFF0C${formatTodoSpan(todo)}`;
|
|
257686
|
+
}
|
|
257687
|
+
function unseenTitle(assigneeName, todo) {
|
|
257688
|
+
return `${assigneeName} \u8FD8\u6CA1\u770B\u8FC7\u8FD9\u6761\uFF1A${todo.title}\uFF0C\u660E\u5929\u622A\u6B62`;
|
|
257689
|
+
}
|
|
257690
|
+
function changedTitle(who, todo, changes, nameOf2) {
|
|
257691
|
+
const parts = changes.map((c5) => `${FIELD_LABEL[c5.field] ?? c5.field} ${shown(c5.field, c5.oldValue, nameOf2)} \u2192 ${shown(c5.field, c5.newValue, nameOf2)}`);
|
|
257692
|
+
const reason = changes.find((c5) => c5.reason)?.reason;
|
|
257693
|
+
return `${who} \u6539\u4E86\u300C${todo.title}\u300D\uFF1A${parts.join("\uFF1B")}${reason ? `\uFF1B\u539F\u56E0\uFF1A${clip4(reason)}` : ""}`;
|
|
257694
|
+
}
|
|
257695
|
+
function statusTitle(who, todo, kind, reason) {
|
|
257696
|
+
const verb = kind === "todo-done" ? "\u5B8C\u6210\u4E86" : kind === "todo-reopened" ? "\u91CD\u65B0\u6253\u5F00\u4E86" : "\u53D6\u6D88\u4E86";
|
|
257697
|
+
return `${who} ${verb}\u300C${todo.title}\u300D${reason ? `\uFF1B\u539F\u56E0\uFF1A${clip4(reason)}` : ""}`;
|
|
257698
|
+
}
|
|
257699
|
+
var VALUE_MAX, FIELD_LABEL, clip4, todoAssignedSourceKey, todoChangeSourceKey, todoUnseenSourceKey;
|
|
257700
|
+
var init_notices = __esm({
|
|
257701
|
+
"../server/src/domains/todos/notices.ts"() {
|
|
257702
|
+
"use strict";
|
|
257703
|
+
init_src();
|
|
257704
|
+
VALUE_MAX = 40;
|
|
257705
|
+
FIELD_LABEL = {
|
|
257706
|
+
title: "\u6807\u9898",
|
|
257707
|
+
note: "\u5907\u6CE8",
|
|
257708
|
+
startDate: "\u5F00\u59CB\u65E5",
|
|
257709
|
+
startTime: "\u5F00\u59CB\u65F6\u523B",
|
|
257710
|
+
dueDate: "\u622A\u6B62\u65E5",
|
|
257711
|
+
dueTime: "\u622A\u6B62\u65F6\u523B",
|
|
257712
|
+
linkedWorkorderId: "\u5173\u8054\u5DE5\u5355",
|
|
257713
|
+
assignee: "\u627F\u63A5\u4EBA"
|
|
257714
|
+
};
|
|
257715
|
+
clip4 = (v2) => v2.length > VALUE_MAX ? `${v2.slice(0, VALUE_MAX)}\u2026` : v2;
|
|
257716
|
+
todoAssignedSourceKey = (todoId) => `todo:${todoId}@assigned`;
|
|
257717
|
+
todoChangeSourceKey = (todoId, changeId) => `todo:${todoId}@change:${changeId}`;
|
|
257718
|
+
todoUnseenSourceKey = (todo) => `todo:${todo.id}@unseen:${todo.dueDate}`;
|
|
257654
257719
|
}
|
|
257720
|
+
});
|
|
257721
|
+
|
|
257722
|
+
// ../server/src/domains/todos/service.ts
|
|
257723
|
+
function canSeeTodo(todo, caller) {
|
|
257724
|
+
return canReadTodo(todo, caller.principal);
|
|
257655
257725
|
}
|
|
257656
|
-
function
|
|
257657
|
-
return
|
|
257726
|
+
function canChangeTodo(todo, caller) {
|
|
257727
|
+
return canWriteTodo(todo, caller.principal);
|
|
257658
257728
|
}
|
|
257659
257729
|
function newTodoId() {
|
|
257660
257730
|
return `td_${(0, import_node_crypto71.randomBytes)(4).toString("hex")}`;
|
|
@@ -257675,16 +257745,18 @@ function assertStartBeforeDue(t) {
|
|
|
257675
257745
|
throw new ApiError(400, "TODO_START_AFTER_DUE", `\u5F00\u59CB\uFF08${fmt(t.startDate, t.startTime)}\uFF09\u4E0D\u80FD\u665A\u4E8E\u622A\u6B62\uFF08${fmt(t.dueDate, t.dueTime)}\uFF09`);
|
|
257676
257746
|
}
|
|
257677
257747
|
}
|
|
257678
|
-
var import_node_crypto71, TITLE_MAX, NOTE_MAX, REASON_MAX, clean2, TodoService;
|
|
257748
|
+
var import_node_crypto71, TITLE_MAX, NOTE_MAX, REASON_MAX, LIST_MAX, clean2, TodoService;
|
|
257679
257749
|
var init_service10 = __esm({
|
|
257680
257750
|
"../server/src/domains/todos/service.ts"() {
|
|
257681
257751
|
"use strict";
|
|
257682
257752
|
import_node_crypto71 = require("node:crypto");
|
|
257683
257753
|
init_src();
|
|
257684
257754
|
init_router();
|
|
257755
|
+
init_notices();
|
|
257685
257756
|
TITLE_MAX = 200;
|
|
257686
257757
|
NOTE_MAX = 4e3;
|
|
257687
257758
|
REASON_MAX = 500;
|
|
257759
|
+
LIST_MAX = 500;
|
|
257688
257760
|
clean2 = (v2) => {
|
|
257689
257761
|
const t = v2?.trim();
|
|
257690
257762
|
return t ? t : void 0;
|
|
@@ -257701,7 +257773,71 @@ var init_service10 = __esm({
|
|
|
257701
257773
|
}
|
|
257702
257774
|
async respond(todo) {
|
|
257703
257775
|
const today = await this.today();
|
|
257704
|
-
return { today, todayLine: formatTodayLine(today), todo };
|
|
257776
|
+
return { today, todayLine: formatTodayLine(today), todo, names: await this.namesFor([todo]) };
|
|
257777
|
+
}
|
|
257778
|
+
async people() {
|
|
257779
|
+
return await this.deps.people?.() ?? [];
|
|
257780
|
+
}
|
|
257781
|
+
/** 列表 / 单条里出现的发起人、承接人的显示名。 */
|
|
257782
|
+
async namesFor(todos, people) {
|
|
257783
|
+
const ids2 = new Set(todos.flatMap((t) => [t.creatorActorId, ...todoAssignees(t)]));
|
|
257784
|
+
const out = {};
|
|
257785
|
+
for (const p4 of people ?? await this.people()) if (ids2.has(p4.actorId)) out[p4.actorId] = p4.name;
|
|
257786
|
+
return out;
|
|
257787
|
+
}
|
|
257788
|
+
/**
|
|
257789
|
+
* 把「人」解析成本公司唯一一个真人(ADR 0640):`me` / `我` = 调用者代表的真人;actorId 必须是本公司成员;
|
|
257790
|
+
* 名字先比全名(昵称、账号名、邮箱、邮箱 @ 前那段,不分大小写),没有再比「包含」——**只认唯一一个**,
|
|
257791
|
+
* 一个都没有或不止一个都报错、列出候选,不替人挑。
|
|
257792
|
+
*/
|
|
257793
|
+
async resolvePerson(raw, caller, what = "\u627F\u63A5\u4EBA") {
|
|
257794
|
+
const input = raw.trim();
|
|
257795
|
+
if (!input) throw new ApiError(400, "TODO_INVALID", `${what}\u4E0D\u80FD\u4E3A\u7A7A`);
|
|
257796
|
+
if (input === "me" || input === "\u6211") return caller.principal;
|
|
257797
|
+
const people = await this.people();
|
|
257798
|
+
if (input.startsWith("actor:")) {
|
|
257799
|
+
if (input === caller.principal) return input;
|
|
257800
|
+
if (people.some((p4) => p4.actorId === input)) return input;
|
|
257801
|
+
throw new ApiError(400, "TODO_PERSON_NOT_MEMBER", `${what} ${input} \u4E0D\u662F\u672C\u516C\u53F8\u7684\u771F\u4EBA\u6210\u5458`);
|
|
257802
|
+
}
|
|
257803
|
+
const key2 = input.toLowerCase();
|
|
257804
|
+
const names = (p4) => [p4.name, ...p4.aliases ?? []].map((n3) => n3.trim().toLowerCase()).filter(Boolean);
|
|
257805
|
+
let hits = people.filter((p4) => names(p4).includes(key2));
|
|
257806
|
+
if (hits.length === 0) hits = people.filter((p4) => names(p4).some((n3) => n3.includes(key2)));
|
|
257807
|
+
const unique4 = [...new Map(hits.map((p4) => [p4.actorId, p4])).values()];
|
|
257808
|
+
if (unique4.length === 1) return unique4[0].actorId;
|
|
257809
|
+
if (unique4.length === 0) throw new ApiError(400, "TODO_PERSON_NOT_FOUND", `\u672C\u516C\u53F8\u6CA1\u6709\u53EB\u300C${input}\u300D\u7684\u6210\u5458\uFF08${what}\uFF09\u3002\u7528 actorId \u6307\u5B9A\uFF0C\u6216\u6362\u4E2A\u5199\u6CD5\u3002`);
|
|
257810
|
+
const list3 = unique4.map((p4) => `${p4.name}\uFF08${p4.actorId}\uFF09`).join("\u3001");
|
|
257811
|
+
throw new ApiError(400, "TODO_PERSON_AMBIGUOUS", `\u300C${input}\u300D\u5BF9\u5F97\u4E0A\u4E0D\u6B62\u4E00\u4E2A\u4EBA\uFF1A${list3}\u3002\u8BF7\u7528 actorId \u6307\u5B9A${what}\u3002`);
|
|
257812
|
+
}
|
|
257813
|
+
/** 给另一方(或承接人)发通知;失败只告警。 */
|
|
257814
|
+
async notice(todo, actor, recipients, kind, sourceKey, title, at2) {
|
|
257815
|
+
if (!this.deps.notify || recipients.length === 0) return;
|
|
257816
|
+
const records = recipients.map((recipient) => ({
|
|
257817
|
+
recipient,
|
|
257818
|
+
category: "todo",
|
|
257819
|
+
sourceKey,
|
|
257820
|
+
kind,
|
|
257821
|
+
title,
|
|
257822
|
+
enteredAt: at2,
|
|
257823
|
+
actorId: actor,
|
|
257824
|
+
// 来源对话是发起人那边的:只有发起人点了能跳回去;给承接人的不带(带了也打不开)。
|
|
257825
|
+
...recipient === todo.creatorActorId && todo.sourceChatSessionId ? { chatSessionId: todo.sourceChatSessionId } : {}
|
|
257826
|
+
}));
|
|
257827
|
+
try {
|
|
257828
|
+
await this.deps.notify(records);
|
|
257829
|
+
} catch (error3) {
|
|
257830
|
+
this.deps.warn?.(new Error(`\u5F85\u529E ${todo.id} \u901A\u77E5\u6CA1\u53D1\u51FA\u53BB\uFF1A${String(error3)}`));
|
|
257831
|
+
}
|
|
257832
|
+
}
|
|
257833
|
+
/** 「另一方」:调用者是发起人 → 承接人们;是承接人 → 发起人。只在派给别人、且已经发出的待办上有。 */
|
|
257834
|
+
counterparts(todo, caller) {
|
|
257835
|
+
if (!isTodoDelegated(todo) || isTodoUnsent(todo)) return [];
|
|
257836
|
+
const parties = [todo.creatorActorId, ...todoAssignees(todo)];
|
|
257837
|
+
return [...new Set(parties)].filter((p4) => p4 !== caller.principal);
|
|
257838
|
+
}
|
|
257839
|
+
async nameOf(id) {
|
|
257840
|
+
return (await this.people()).find((p4) => p4.actorId === id)?.name ?? id;
|
|
257705
257841
|
}
|
|
257706
257842
|
change(todoId, caller, at2, field, oldValue, newValue, reason) {
|
|
257707
257843
|
return {
|
|
@@ -257731,8 +257867,10 @@ var init_service10 = __esm({
|
|
|
257731
257867
|
}
|
|
257732
257868
|
assertStartBeforeDue({ ...input });
|
|
257733
257869
|
const creator = caller.principal;
|
|
257734
|
-
const assignee = caller
|
|
257735
|
-
|
|
257870
|
+
const assignee = input.assignee !== void 0 ? await this.resolvePerson(input.assignee, caller) : creator;
|
|
257871
|
+
const delegated = assignee !== creator;
|
|
257872
|
+
const sourceChatSessionId = clean2(input.sourceChatSessionId);
|
|
257873
|
+
const sourceRunId = delegated && sourceChatSessionId ? this.deps.runOf?.(sourceChatSessionId) : void 0;
|
|
257736
257874
|
const at2 = new Date(this.nowMs()).toISOString();
|
|
257737
257875
|
const todo = {
|
|
257738
257876
|
id: newTodoId(),
|
|
@@ -257745,27 +257883,93 @@ var init_service10 = __esm({
|
|
|
257745
257883
|
...input.dueTime ? { dueTime: input.dueTime } : {},
|
|
257746
257884
|
...input.startDate ? { startDate: input.startDate } : {},
|
|
257747
257885
|
...input.startTime ? { startTime: input.startTime } : {},
|
|
257748
|
-
status: "open",
|
|
257749
|
-
...
|
|
257886
|
+
status: delegated ? "draft" : "open",
|
|
257887
|
+
...sourceChatSessionId ? { sourceChatSessionId } : {},
|
|
257888
|
+
...sourceRunId ? { sourceRunId } : {},
|
|
257750
257889
|
createdAt: at2,
|
|
257751
257890
|
updatedAt: at2
|
|
257752
257891
|
};
|
|
257753
257892
|
const saved = await this.deps.store.create(todo, [this.change(todo.id, caller, at2, "created", void 0, title)]);
|
|
257754
257893
|
return this.respond(saved);
|
|
257755
257894
|
}
|
|
257756
|
-
|
|
257757
|
-
|
|
257758
|
-
|
|
257895
|
+
/**
|
|
257896
|
+
* 列表(ADR 0640):筛选都不给 = 「我承接的、未完成的」(与 0635 相同)。人可以写 actorId、成员名或 `me`;
|
|
257897
|
+
* 从没发出过的草案只列给发起人(存储层与 `canReadTodo` 配对的那条条件)。
|
|
257898
|
+
*/
|
|
257899
|
+
async list(caller, query = {}) {
|
|
257900
|
+
const statuses2 = query.all ? TODO_STATUSES : query.statuses?.length ? query.statuses : ["open"];
|
|
257901
|
+
for (const st of statuses2) if (!TODO_STATUSES.includes(st)) throw new ApiError(400, "TODO_INVALID", `status \u53EA\u6536 ${TODO_STATUSES.join(" / ")}\uFF0C\u6536\u5230\uFF1A${st}`);
|
|
257902
|
+
if (query.dueFrom !== void 0) assertDay(query.dueFrom, "dueFrom");
|
|
257903
|
+
if (query.dueTo !== void 0) assertDay(query.dueTo, "dueTo");
|
|
257904
|
+
const assignee = query.assignee !== void 0 ? await this.resolvePerson(query.assignee, caller) : void 0;
|
|
257905
|
+
const creator = query.creator !== void 0 ? await this.resolvePerson(query.creator, caller, "\u53D1\u8D77\u4EBA") : void 0;
|
|
257906
|
+
const mine = assignee === void 0 && creator === void 0 && !query.everyone;
|
|
257907
|
+
const q4 = clean2(query.q);
|
|
257908
|
+
const items = await this.deps.store.search({
|
|
257909
|
+
statuses: statuses2,
|
|
257910
|
+
...mine ? { assignee: caller.principal } : assignee ? { assignee } : {},
|
|
257911
|
+
...creator ? { creator } : {},
|
|
257912
|
+
...query.dueFrom ? { dueFrom: query.dueFrom } : {},
|
|
257913
|
+
...query.dueTo ? { dueTo: query.dueTo } : {},
|
|
257914
|
+
...q4 ? { q: q4 } : {},
|
|
257915
|
+
...clean2(query.chatSessionId) ? { chatSessionId: clean2(query.chatSessionId) } : {},
|
|
257916
|
+
viewer: caller.principal,
|
|
257917
|
+
limit: LIST_MAX
|
|
257918
|
+
});
|
|
257919
|
+
const visible = items.filter((t) => canSeeTodo(t, caller));
|
|
257759
257920
|
const today = await this.today();
|
|
257760
|
-
return { today, todayLine: formatTodayLine(today), items };
|
|
257921
|
+
return { today, todayLine: formatTodayLine(today), items: visible, names: await this.namesFor(visible) };
|
|
257761
257922
|
}
|
|
257762
|
-
/**
|
|
257923
|
+
/**
|
|
257924
|
+
* 读一条并校验读权限。不存在 404;从没发出过的草案对发起人以外的人也是 404(不透露有这么一条)。
|
|
257925
|
+
*/
|
|
257763
257926
|
async get(caller, id) {
|
|
257764
257927
|
const todo = await this.deps.store.get(id);
|
|
257765
|
-
if (!todo) throw new ApiError(404, "TODO_NOT_FOUND", `\u5F85\u529E ${id} \u4E0D\u5B58\u5728`);
|
|
257766
|
-
|
|
257928
|
+
if (!todo || !canSeeTodo(todo, caller)) throw new ApiError(404, "TODO_NOT_FOUND", `\u5F85\u529E ${id} \u4E0D\u5B58\u5728`);
|
|
257929
|
+
return todo;
|
|
257930
|
+
}
|
|
257931
|
+
/** 读一条并校验改权限:读得到但不是当事人 403(任务书口径「其他人 403」)。 */
|
|
257932
|
+
async getForChange(caller, id) {
|
|
257933
|
+
const todo = await this.get(caller, id);
|
|
257934
|
+
if (!canChangeTodo(todo, caller)) throw new ApiError(403, "TODO_FORBIDDEN", "\u53EA\u6709\u53D1\u8D77\u4EBA\u6216\u627F\u63A5\u4EBA\u80FD\u6539\u8FD9\u6761\u5F85\u529E");
|
|
257767
257935
|
return todo;
|
|
257768
257936
|
}
|
|
257937
|
+
/**
|
|
257938
|
+
* `oasis todo show`:单条 + 全部变更记录。承接人(派给别人的、已发出的)第一次看时记下 `firstViewedAt`
|
|
257939
|
+
* (「对方还没看过」提醒用,ADR 0640);发起人、第三人看不算。
|
|
257940
|
+
*/
|
|
257941
|
+
async view(caller, id) {
|
|
257942
|
+
let todo = await this.get(caller, id);
|
|
257943
|
+
if (!todo.firstViewedAt && !isTodoUnsent(todo) && isTodoAssignedToByOther(todo, caller.principal)) {
|
|
257944
|
+
todo = await this.deps.store.markFirstViewed(todo.id, new Date(this.nowMs()).toISOString()) ?? todo;
|
|
257945
|
+
}
|
|
257946
|
+
return { todo, changes: await this.deps.store.listChanges(id), names: await this.namesFor([todo]) };
|
|
257947
|
+
}
|
|
257948
|
+
/**
|
|
257949
|
+
* 发出(ADR 0640):草案 → open,记 `sentAt`,通知承接人「X 派给你:…」。
|
|
257950
|
+
* **只有发起人本人**能发——经助理(agent)调用一律 403:「先出草案、人点发出」就是为了不让助理替人发出去。
|
|
257951
|
+
* 已经发出过的再点一次原样返回(连点 / 刷新后重点不报错、不重发通知)。
|
|
257952
|
+
*/
|
|
257953
|
+
async send(caller, id) {
|
|
257954
|
+
const before = await this.get(caller, id);
|
|
257955
|
+
if (before.creatorActorId !== caller.principal) throw new ApiError(403, "TODO_FORBIDDEN", "\u53EA\u6709\u53D1\u8D77\u4EBA\u80FD\u53D1\u51FA\u8FD9\u6761\u5F85\u529E");
|
|
257956
|
+
if (caller.hand) throw new ApiError(403, "TODO_SEND_HUMAN_ONLY", "\u6D3E\u7ED9\u522B\u4EBA\u7684\u5F85\u529E\u53EA\u80FD\u7531\u53D1\u8D77\u4EBA\u672C\u4EBA\u5728\u8349\u6848\u5361\u4E0A\u70B9\u300C\u53D1\u51FA\u300D\uFF0C\u52A9\u7406\u4E0D\u80FD\u4EE3\u53D1\u3002");
|
|
257957
|
+
if (before.sentAt) return this.respond(before);
|
|
257958
|
+
if (before.status !== "draft") throw new ApiError(409, "TODO_NOT_DRAFT", "\u8FD9\u6761\u5F85\u529E\u4E0D\u662F\u8349\u6848\uFF08\u53EF\u80FD\u5DF2\u53D6\u6D88\uFF09\uFF0C\u4E0D\u80FD\u53D1\u51FA");
|
|
257959
|
+
const at2 = new Date(this.nowMs()).toISOString();
|
|
257960
|
+
const next = { ...before, status: "open", sentAt: at2, updatedAt: at2, recordedByActorId: caller.actor };
|
|
257961
|
+
const out = await this.save(caller, before, next, [this.change(id, caller, at2, "sent", void 0, before.assigneeActorId)]);
|
|
257962
|
+
await this.notice(
|
|
257963
|
+
out.todo,
|
|
257964
|
+
caller.principal,
|
|
257965
|
+
todoAssignees(out.todo),
|
|
257966
|
+
"todo-assigned",
|
|
257967
|
+
todoAssignedSourceKey(id),
|
|
257968
|
+
assignedTitle(await this.nameOf(caller.principal), out.todo),
|
|
257969
|
+
at2
|
|
257970
|
+
);
|
|
257971
|
+
return out;
|
|
257972
|
+
}
|
|
257769
257973
|
async save(caller, before, next, changes) {
|
|
257770
257974
|
if (changes.length === 0) return this.respond(before);
|
|
257771
257975
|
const saved = await this.deps.store.update(next, before.updatedAt, changes);
|
|
@@ -257773,7 +257977,7 @@ var init_service10 = __esm({
|
|
|
257773
257977
|
return this.respond(saved);
|
|
257774
257978
|
}
|
|
257775
257979
|
async edit(caller, id, input) {
|
|
257776
|
-
const before = await this.
|
|
257980
|
+
const before = await this.getForChange(caller, id);
|
|
257777
257981
|
const reason = clean2(input.reason);
|
|
257778
257982
|
if (reason && reason.length > REASON_MAX) throw new ApiError(400, "TODO_INVALID", `reason \u4E0D\u80FD\u8D85\u8FC7 ${REASON_MAX} \u5B57`);
|
|
257779
257983
|
const at2 = new Date(this.nowMs()).toISOString();
|
|
@@ -257819,29 +258023,75 @@ var init_service10 = __esm({
|
|
|
257819
258023
|
set("dueTime", input.dueTime ?? void 0);
|
|
257820
258024
|
}
|
|
257821
258025
|
if (input.linkedWorkorderId !== void 0) set("linkedWorkorderId", clean2(input.linkedWorkorderId));
|
|
258026
|
+
if (input.assignee !== void 0) {
|
|
258027
|
+
if (!isTodoUnsent(before)) throw new ApiError(400, "TODO_ASSIGNEE_LOCKED", "\u5DF2\u7ECF\u53D1\u51FA\u7684\u5F85\u529E\u4E0D\u80FD\u6362\u627F\u63A5\u4EBA\uFF1A\u53D6\u6D88\u8FD9\u6761\uFF08\u5E26\u539F\u56E0\uFF09\uFF0C\u518D\u91CD\u65B0\u6D3E\u4E00\u6761\u3002");
|
|
258028
|
+
const assignee = await this.resolvePerson(input.assignee, caller);
|
|
258029
|
+
if (assignee === before.creatorActorId) throw new ApiError(400, "TODO_INVALID", "\u8349\u6848\u4E0D\u80FD\u6539\u6210\u6D3E\u7ED9\u81EA\u5DF1\uFF1A\u53D6\u6D88\u8FD9\u6761\u8349\u6848\uFF0C\u518D\u7ED9\u81EA\u5DF1\u8BB0\u4E00\u6761\u3002");
|
|
258030
|
+
if (assignee !== before.assigneeActorId) {
|
|
258031
|
+
next.assigneeActorId = assignee;
|
|
258032
|
+
changes.push(this.change(id, caller, at2, "assignee", before.assigneeActorId, assignee, reason));
|
|
258033
|
+
}
|
|
258034
|
+
}
|
|
257822
258035
|
if (next.startTime && !next.startDate) throw new ApiError(400, "TODO_INVALID", "\u6709\u5F00\u59CB\u65F6\u523B\u5C31\u8981\u6709\u5F00\u59CB\u65E5\uFF08\u5148\u8BBE startDate\uFF09");
|
|
257823
258036
|
assertStartBeforeDue(next);
|
|
258037
|
+
if (!reason && isTodoAssignedToByOther(before, caller.principal) && changes.some((c5) => c5.field === "dueDate" || c5.field === "dueTime")) {
|
|
258038
|
+
throw new ApiError(400, "TODO_REASON_REQUIRED", "\u8FD9\u6761\u662F\u522B\u4EBA\u6D3E\u7ED9\u4F60\u7684\uFF1A\u6539\u622A\u6B62\u8981\u5199\u539F\u56E0\uFF08--reason\uFF09\uFF0C\u53D1\u8D77\u4EBA\u4F1A\u6536\u5230\u901A\u77E5\u3002");
|
|
258039
|
+
}
|
|
257824
258040
|
if (changes.length > 0) {
|
|
257825
258041
|
next.updatedAt = at2;
|
|
257826
258042
|
next.recordedByActorId = caller.actor;
|
|
257827
258043
|
}
|
|
257828
|
-
|
|
258044
|
+
const out = await this.save(caller, before, next, changes);
|
|
258045
|
+
if (changes.length > 0) {
|
|
258046
|
+
const people = await this.people();
|
|
258047
|
+
const nameOf2 = (a5) => people.find((p4) => p4.actorId === a5)?.name ?? a5;
|
|
258048
|
+
await this.notice(
|
|
258049
|
+
out.todo,
|
|
258050
|
+
caller.principal,
|
|
258051
|
+
this.counterparts(out.todo, caller),
|
|
258052
|
+
"todo-changed",
|
|
258053
|
+
todoChangeSourceKey(id, changes[0].id),
|
|
258054
|
+
changedTitle(nameOf2(caller.principal), out.todo, changes, nameOf2),
|
|
258055
|
+
at2
|
|
258056
|
+
);
|
|
258057
|
+
}
|
|
258058
|
+
return out;
|
|
257829
258059
|
}
|
|
257830
|
-
|
|
257831
|
-
|
|
258060
|
+
/**
|
|
258061
|
+
* 完成 / 取消 / 重新打开。草案(从没发出过的):不能完成;取消了也仍只有发起人看得到;重新打开回到草案、不会因此发出去。
|
|
258062
|
+
* 别人派给我的,取消必须带原因(ADR 0640 D9)。派给别人且已发出的,动完通知另一方。
|
|
258063
|
+
*/
|
|
258064
|
+
async setStatus(caller, id, requested, reason) {
|
|
258065
|
+
const before = await this.getForChange(caller, id);
|
|
257832
258066
|
const why = clean2(reason);
|
|
257833
258067
|
if (why && why.length > REASON_MAX) throw new ApiError(400, "TODO_INVALID", `reason \u4E0D\u80FD\u8D85\u8FC7 ${REASON_MAX} \u5B57`);
|
|
258068
|
+
const unsent = isTodoUnsent(before);
|
|
258069
|
+
if (unsent && requested === "done") throw new ApiError(400, "TODO_NOT_SENT", "\u8FD9\u6761\u8FD8\u662F\u8349\u6848\uFF08\u6CA1\u53D1\u51FA\uFF09\uFF0C\u4E0D\u80FD\u6807\u5B8C\u6210\uFF1A\u5148\u5728\u8349\u6848\u5361\u4E0A\u70B9\u300C\u53D1\u51FA\u300D\uFF0C\u6216\u53D6\u6D88\u5B83\u3002");
|
|
258070
|
+
if (before.status === "draft" && requested === "open") throw new ApiError(400, "TODO_NOT_SENT", "\u8FD9\u6761\u662F\u8349\u6848\uFF1A\u8981\u751F\u6548\u8BF7\u53D1\u8D77\u4EBA\u5728\u8349\u6848\u5361\u4E0A\u70B9\u300C\u53D1\u51FA\u300D\u3002");
|
|
258071
|
+
const status = unsent && requested === "open" ? "draft" : requested;
|
|
257834
258072
|
if (before.status === status) return this.respond(before);
|
|
258073
|
+
if (status === "cancelled" && !why && isTodoAssignedToByOther(before, caller.principal)) {
|
|
258074
|
+
throw new ApiError(400, "TODO_REASON_REQUIRED", "\u8FD9\u6761\u662F\u522B\u4EBA\u6D3E\u7ED9\u4F60\u7684\uFF1A\u53D6\u6D88\u8981\u5199\u539F\u56E0\uFF08--reason\uFF09\uFF0C\u53D1\u8D77\u4EBA\u4F1A\u6536\u5230\u901A\u77E5\u3002");
|
|
258075
|
+
}
|
|
257835
258076
|
const at2 = new Date(this.nowMs()).toISOString();
|
|
257836
258077
|
const next = { ...before, status, updatedAt: at2, recordedByActorId: caller.actor };
|
|
257837
258078
|
delete next.doneAt;
|
|
257838
258079
|
delete next.cancelledAt;
|
|
257839
258080
|
if (status === "done") next.doneAt = at2;
|
|
257840
258081
|
if (status === "cancelled") next.cancelledAt = at2;
|
|
257841
|
-
|
|
257842
|
-
|
|
257843
|
-
|
|
257844
|
-
|
|
258082
|
+
const change = this.change(id, caller, at2, "status", before.status, status, why);
|
|
258083
|
+
const out = await this.save(caller, before, next, [change]);
|
|
258084
|
+
const kind = status === "done" ? "todo-done" : status === "cancelled" ? "todo-cancelled" : "todo-reopened";
|
|
258085
|
+
await this.notice(
|
|
258086
|
+
out.todo,
|
|
258087
|
+
caller.principal,
|
|
258088
|
+
this.counterparts(out.todo, caller),
|
|
258089
|
+
kind,
|
|
258090
|
+
todoChangeSourceKey(id, change.id),
|
|
258091
|
+
statusTitle(await this.nameOf(caller.principal), out.todo, kind, why),
|
|
258092
|
+
at2
|
|
258093
|
+
);
|
|
258094
|
+
return out;
|
|
257845
258095
|
}
|
|
257846
258096
|
};
|
|
257847
258097
|
}
|
|
@@ -257905,15 +258155,29 @@ var init_store3 = __esm({
|
|
|
257905
258155
|
return this.write((rows) => {
|
|
257906
258156
|
const current = rows.todos[next.id];
|
|
257907
258157
|
if (!current || current.updatedAt !== expectedUpdatedAt) return null;
|
|
257908
|
-
|
|
258158
|
+
const firstViewedAt = current.firstViewedAt ?? next.firstViewedAt;
|
|
258159
|
+
rows.todos[next.id] = structuredClone({ ...next, ...firstViewedAt ? { firstViewedAt } : {} });
|
|
257909
258160
|
rows.changes.push(...structuredClone(changes));
|
|
257910
|
-
return next;
|
|
258161
|
+
return rows.todos[next.id];
|
|
257911
258162
|
});
|
|
257912
258163
|
}
|
|
257913
258164
|
async listInvolving(actorId, statuses2) {
|
|
257914
258165
|
await this.tail;
|
|
257915
258166
|
return Object.values(this.rows.todos).filter((t) => statuses2.includes(t.status) && (t.creatorActorId === actorId || todoAssignees(t).includes(actorId))).sort(compareTodos).map((t) => structuredClone(t));
|
|
257916
258167
|
}
|
|
258168
|
+
async search(f6) {
|
|
258169
|
+
await this.tail;
|
|
258170
|
+
const q4 = f6.q?.toLowerCase();
|
|
258171
|
+
return Object.values(this.rows.todos).filter((t) => f6.statuses.includes(t.status) && (!f6.assignee || todoAssignees(t).includes(f6.assignee)) && (!f6.creator || t.creatorActorId === f6.creator) && (!f6.dueFrom || t.dueDate >= f6.dueFrom) && (!f6.dueTo || t.dueDate <= f6.dueTo) && (!f6.chatSessionId || t.sourceChatSessionId === f6.chatSessionId) && (!q4 || t.title.toLowerCase().includes(q4) || (t.note ?? "").toLowerCase().includes(q4)) && canReadTodo(t, f6.viewer)).sort(compareTodos).slice(0, f6.limit).map((t) => structuredClone(t));
|
|
258172
|
+
}
|
|
258173
|
+
markFirstViewed(id, at2) {
|
|
258174
|
+
return this.write((rows) => {
|
|
258175
|
+
const t = rows.todos[id];
|
|
258176
|
+
if (!t) return null;
|
|
258177
|
+
t.firstViewedAt ??= at2;
|
|
258178
|
+
return t;
|
|
258179
|
+
}).then((r5) => r5 ?? this.get(id));
|
|
258180
|
+
}
|
|
257917
258181
|
async listOpenReminderCandidates(fromDay, toDay) {
|
|
257918
258182
|
await this.tail;
|
|
257919
258183
|
const within = (day) => day !== void 0 && day >= fromDay && day <= toDay;
|
|
@@ -257943,18 +258207,32 @@ function todosDomain(options) {
|
|
|
257943
258207
|
const sourceSession = (req, fromBody) => req.auth.dispatch?.chatSessionId ?? fromBody;
|
|
257944
258208
|
return (router) => {
|
|
257945
258209
|
router.get("/api/todos", wrap(async (req, service, caller) => {
|
|
257946
|
-
const
|
|
257947
|
-
|
|
258210
|
+
const q4 = req.query;
|
|
258211
|
+
const flag = (k6) => ["1", "true"].includes((q4.get(k6) ?? "").toLowerCase());
|
|
258212
|
+
const one2 = (k6) => q4.get(k6) ?? void 0;
|
|
258213
|
+
const statuses2 = q4.getAll("status").flatMap((v2) => v2.split(",")).map((v2) => v2.trim()).filter(Boolean);
|
|
258214
|
+
return {
|
|
258215
|
+
status: 200,
|
|
258216
|
+
body: await service.list(caller, {
|
|
258217
|
+
all: flag("all"),
|
|
258218
|
+
everyone: flag("everyone"),
|
|
258219
|
+
...statuses2.length ? { statuses: statuses2 } : {},
|
|
258220
|
+
...one2("assignee") !== void 0 ? { assignee: one2("assignee") } : {},
|
|
258221
|
+
...one2("creator") !== void 0 ? { creator: one2("creator") } : {},
|
|
258222
|
+
...one2("dueFrom") !== void 0 ? { dueFrom: one2("dueFrom") } : {},
|
|
258223
|
+
...one2("dueTo") !== void 0 ? { dueTo: one2("dueTo") } : {},
|
|
258224
|
+
...one2("q") !== void 0 ? { q: one2("q") } : {},
|
|
258225
|
+
...one2("chatSessionId") !== void 0 ? { chatSessionId: one2("chatSessionId") } : {}
|
|
258226
|
+
})
|
|
258227
|
+
};
|
|
257948
258228
|
}));
|
|
257949
258229
|
router.post("/api/todos", wrap(async (req, service, caller) => {
|
|
257950
258230
|
const body2 = create.parse(req.body ?? {});
|
|
257951
258231
|
const sourceChatSessionId = sourceSession(req, body2.sourceChatSessionId);
|
|
257952
258232
|
return { status: 201, body: await service.create(caller, { ...body2, ...sourceChatSessionId ? { sourceChatSessionId } : {} }) };
|
|
257953
258233
|
}));
|
|
257954
|
-
router.get("/api/todos/:id", wrap(async (req, service, caller) => {
|
|
257955
|
-
|
|
257956
|
-
return { status: 200, body: { todo, changes: await service.history(caller, todo.id) } };
|
|
257957
|
-
}));
|
|
258234
|
+
router.get("/api/todos/:id", wrap(async (req, service, caller) => ({ status: 200, body: await service.view(caller, req.params.id) })));
|
|
258235
|
+
router.post("/api/todos/:id/send", wrap(async (req, service, caller) => ({ status: 200, body: await service.send(caller, req.params.id) })));
|
|
257958
258236
|
router.patch("/api/todos/:id", wrap(async (req, service, caller) => {
|
|
257959
258237
|
const body2 = edit.parse(req.body ?? {});
|
|
257960
258238
|
return { status: 200, body: await service.edit(caller, req.params.id, body2) };
|
|
@@ -257975,6 +258253,7 @@ var init_routes11 = __esm({
|
|
|
257975
258253
|
init_zod();
|
|
257976
258254
|
init_router();
|
|
257977
258255
|
create = external_exports.object({
|
|
258256
|
+
assignee: external_exports.string().optional(),
|
|
257978
258257
|
title: external_exports.string(),
|
|
257979
258258
|
note: external_exports.string().optional(),
|
|
257980
258259
|
dueDate: external_exports.string(),
|
|
@@ -257991,6 +258270,7 @@ var init_routes11 = __esm({
|
|
|
257991
258270
|
startDate: external_exports.string().nullable().optional(),
|
|
257992
258271
|
startTime: external_exports.string().nullable().optional(),
|
|
257993
258272
|
linkedWorkorderId: external_exports.string().nullable().optional(),
|
|
258273
|
+
assignee: external_exports.string().optional(),
|
|
257994
258274
|
reason: external_exports.string().optional()
|
|
257995
258275
|
}).strict();
|
|
257996
258276
|
statusBody = external_exports.object({ reason: external_exports.string().optional() }).strict();
|
|
@@ -258029,6 +258309,7 @@ var init_reminders = __esm({
|
|
|
258029
258309
|
"../server/src/domains/todos/reminders.ts"() {
|
|
258030
258310
|
"use strict";
|
|
258031
258311
|
init_src();
|
|
258312
|
+
init_notices();
|
|
258032
258313
|
TODO_REMINDER_TIME = "09:00";
|
|
258033
258314
|
TODO_REMINDER_SCAN_MS = 6e4;
|
|
258034
258315
|
TODO_REMINDER_TITLE = {
|
|
@@ -258086,16 +258367,48 @@ var init_reminders = __esm({
|
|
|
258086
258367
|
kind: r5.kind,
|
|
258087
258368
|
title: TODO_REMINDER_TITLE[r5.kind](todo.title),
|
|
258088
258369
|
enteredAt,
|
|
258089
|
-
...todo.sourceChatSessionId ? { chatSessionId: todo.sourceChatSessionId } : {}
|
|
258370
|
+
...todo.sourceChatSessionId && recipient === todo.creatorActorId ? { chatSessionId: todo.sourceChatSessionId } : {}
|
|
258090
258371
|
});
|
|
258091
258372
|
this.seen.add(key2);
|
|
258092
258373
|
if (row.enteredAt === enteredAt) fresh.push(row);
|
|
258093
258374
|
}
|
|
258375
|
+
if (r5.kind === "todo-due-1d") {
|
|
258376
|
+
const row = await this.unseen(companyId, ledger, todo, enteredAt);
|
|
258377
|
+
if (row) fresh.push(row);
|
|
258378
|
+
}
|
|
258094
258379
|
}
|
|
258095
258380
|
}
|
|
258096
258381
|
if (fresh.length > 0) this.deps.onInserted?.(companyId, fresh);
|
|
258097
258382
|
return fresh.length;
|
|
258098
258383
|
}
|
|
258384
|
+
/** 「对方还没看过」:三条都成立才发给发起人(见文件头)。返回这一拍新写的那条。 */
|
|
258385
|
+
async unseen(companyId, ledger, todo, enteredAt) {
|
|
258386
|
+
if (!isTodoDelegated(todo) || !todo.sentAt || todo.firstViewedAt) return void 0;
|
|
258387
|
+
const sourceKey = todoUnseenSourceKey(todo);
|
|
258388
|
+
const key2 = JSON.stringify([companyId, todo.creatorActorId, sourceKey]);
|
|
258389
|
+
if (this.seen.has(key2)) return void 0;
|
|
258390
|
+
const assignees = todoAssignees(todo);
|
|
258391
|
+
for (const a5 of assignees) {
|
|
258392
|
+
const assigned = await ledger.findBySource(a5, todoAssignedSourceKey(todo.id));
|
|
258393
|
+
if (!assigned || assigned.readAt) {
|
|
258394
|
+
this.seen.add(key2);
|
|
258395
|
+
return void 0;
|
|
258396
|
+
}
|
|
258397
|
+
}
|
|
258398
|
+
const names = await Promise.all(assignees.map((a5) => this.deps.nameOf?.(companyId, a5) ?? Promise.resolve(a5)));
|
|
258399
|
+
const row = await ledger.insert({
|
|
258400
|
+
recipient: todo.creatorActorId,
|
|
258401
|
+
category: "todo",
|
|
258402
|
+
sourceKey,
|
|
258403
|
+
kind: "todo-unseen",
|
|
258404
|
+
title: unseenTitle(names.join("\u3001"), todo),
|
|
258405
|
+
enteredAt,
|
|
258406
|
+
...assignees[0] ? { actorId: assignees[0] } : {},
|
|
258407
|
+
...todo.sourceChatSessionId ? { chatSessionId: todo.sourceChatSessionId } : {}
|
|
258408
|
+
});
|
|
258409
|
+
this.seen.add(key2);
|
|
258410
|
+
return row.enteredAt === enteredAt ? row : void 0;
|
|
258411
|
+
}
|
|
258099
258412
|
/** 首拍延后一点(默认 10 秒,与通知对账器同款):等 serve 把推送、台账都装好再扫。 */
|
|
258100
258413
|
start(intervalMs = TODO_REMINDER_SCAN_MS, firstDelayMs = 1e4) {
|
|
258101
258414
|
if (this.timer) return;
|
|
@@ -258123,6 +258436,7 @@ var init_todos2 = __esm({
|
|
|
258123
258436
|
init_store3();
|
|
258124
258437
|
init_routes11();
|
|
258125
258438
|
init_reminders();
|
|
258439
|
+
init_notices();
|
|
258126
258440
|
}
|
|
258127
258441
|
});
|
|
258128
258442
|
|
|
@@ -280158,29 +280472,33 @@ var init_externalized_blobs = __esm({
|
|
|
280158
280472
|
return { rows: Number(r5.rows ?? 0), bytes: Number(r5.bytes ?? 0) };
|
|
280159
280473
|
}
|
|
280160
280474
|
/**
|
|
280161
|
-
*
|
|
280162
|
-
*
|
|
280163
|
-
*
|
|
280475
|
+
* 清空一行的 bytea(§6.2 第 6 步 `--purge-pg-bytes` 的单行原语):**先确认对象在后端、大小与行一致**才清——
|
|
280476
|
+
* bytea 是清掉前最后一份副本,对象丢了(误删、恢复不全)时宁可留着。对象缺失 / 大小不符、或核对本身出错
|
|
280477
|
+
* (S3 超时 / 403 / 5xx、fs I/O 错)一律抛错,bytea 不动。可重复执行:已清过或已不是 `ext` 的行跳过。
|
|
280478
|
+
* 返回本行清掉的字节数(行 `size`);跳过返回 0。
|
|
280164
280479
|
*/
|
|
280165
|
-
async
|
|
280166
|
-
if (hashes.length === 0) return { purged: 0, bytes: 0, unverified: [] };
|
|
280480
|
+
async purgePgBytesOne(hash3) {
|
|
280167
280481
|
const res = await this.pool.query(
|
|
280168
|
-
`SELECT
|
|
280169
|
-
[
|
|
280482
|
+
`SELECT size FROM ${this.t} WHERE hash = $1 AND location = 'ext' AND bytes IS NOT NULL`,
|
|
280483
|
+
[hash3]
|
|
280170
280484
|
);
|
|
280171
|
-
const
|
|
280172
|
-
|
|
280173
|
-
|
|
280174
|
-
|
|
280175
|
-
|
|
280176
|
-
|
|
280177
|
-
}
|
|
280178
|
-
|
|
280485
|
+
const row = res.rows[0];
|
|
280486
|
+
if (!row) return 0;
|
|
280487
|
+
const size = Number(row.size);
|
|
280488
|
+
let head;
|
|
280489
|
+
try {
|
|
280490
|
+
head = await this.backend.head(this.key(hash3));
|
|
280491
|
+
} catch (err) {
|
|
280492
|
+
throw new Error("\u6838\u5BF9\u5BF9\u8C61\u51FA\u9519\uFF0Cbytea \u4FDD\u7559", { cause: err });
|
|
280493
|
+
}
|
|
280494
|
+
if (!head || head.size !== size) {
|
|
280495
|
+
throw new Error(`\u5BF9\u8C61\u4E0D\u5728\u540E\u7AEF\u6216\u5927\u5C0F\u4E0E\u884C\u4E0D\u7B26\uFF08\u884C ${size} B\uFF0C\u5BF9\u8C61 ${head ? `${head.size} B` : "\u4E0D\u5B58\u5728"}\uFF09\uFF0Cbytea \u4FDD\u7559`);
|
|
280496
|
+
}
|
|
280179
280497
|
const upd = await this.pool.query(
|
|
280180
|
-
`UPDATE ${this.t} SET bytes = NULL WHERE hash =
|
|
280181
|
-
[
|
|
280498
|
+
`UPDATE ${this.t} SET bytes = NULL WHERE hash = $1 AND location = 'ext' AND bytes IS NOT NULL`,
|
|
280499
|
+
[hash3]
|
|
280182
280500
|
);
|
|
280183
|
-
return
|
|
280501
|
+
return (upd.rowCount ?? 0) > 0 ? size : 0;
|
|
280184
280502
|
}
|
|
280185
280503
|
/** 观察期后清空已外部化行的 bytea(§6.2 第 6 步),一批一条语句。返回本批清掉的行数。 */
|
|
280186
280504
|
async purgePgBytes(limit) {
|
|
@@ -320552,27 +320870,19 @@ async function migrateBlobs(store, op2, opts) {
|
|
|
320552
320870
|
}
|
|
320553
320871
|
const hashes = await store.listHashes(selector, after, batch);
|
|
320554
320872
|
if (hashes.length === 0) break;
|
|
320555
|
-
|
|
320556
|
-
|
|
320557
|
-
|
|
320558
|
-
|
|
320559
|
-
for (const h5 of r5.unverified) fail(h5, new Error("\u5BF9\u8C61\u4E0D\u5728\u540E\u7AEF\u6216\u5927\u5C0F\u4E0E\u884C\u4E0D\u7B26\uFF0Cbytea \u4FDD\u7559"));
|
|
320560
|
-
after = hashes[hashes.length - 1];
|
|
320561
|
-
} else {
|
|
320562
|
-
for (const hash3 of hashes) {
|
|
320563
|
-
if (stop()) {
|
|
320564
|
-
report.interrupted = true;
|
|
320565
|
-
break;
|
|
320566
|
-
}
|
|
320567
|
-
try {
|
|
320568
|
-
const n3 = op2 === "externalize" ? await store.externalizeOne(hash3) : await store.internalizeOne(hash3);
|
|
320569
|
-
report.rows++;
|
|
320570
|
-
report.bytes += n3;
|
|
320571
|
-
} catch (err) {
|
|
320572
|
-
fail(hash3, err);
|
|
320573
|
-
}
|
|
320574
|
-
after = hash3;
|
|
320873
|
+
for (const hash3 of hashes) {
|
|
320874
|
+
if (stop()) {
|
|
320875
|
+
report.interrupted = true;
|
|
320876
|
+
break;
|
|
320575
320877
|
}
|
|
320878
|
+
try {
|
|
320879
|
+
const n3 = op2 === "externalize" ? await store.externalizeOne(hash3) : op2 === "internalize" ? await store.internalizeOne(hash3) : await store.purgePgBytesOne(hash3);
|
|
320880
|
+
report.rows++;
|
|
320881
|
+
report.bytes += n3;
|
|
320882
|
+
} catch (err) {
|
|
320883
|
+
fail(hash3, err);
|
|
320884
|
+
}
|
|
320885
|
+
after = hash3;
|
|
320576
320886
|
}
|
|
320577
320887
|
report.batches++;
|
|
320578
320888
|
opts.onProgress?.(progressOf(report));
|
|
@@ -322376,10 +322686,10 @@ __export(src_exports2, {
|
|
|
322376
322686
|
assertBindingWritePermission: () => assertBindingWritePermission,
|
|
322377
322687
|
assertCompanyManager: () => assertCompanyManager,
|
|
322378
322688
|
assertCreatableVariable: () => assertCreatableVariable,
|
|
322379
|
-
assertFirstVersionAssignee: () => assertFirstVersionAssignee,
|
|
322380
322689
|
assertMayCreateAgent: () => assertMayCreateAgent,
|
|
322381
322690
|
assertMayManageAgent: () => assertMayManageAgent,
|
|
322382
322691
|
assertMaySelfOrManage: () => assertMaySelfOrManage,
|
|
322692
|
+
assignedTitle: () => assignedTitle,
|
|
322383
322693
|
assistantBoundToMember: () => assistantBoundToMember,
|
|
322384
322694
|
assistantNameFor: () => assistantNameFor,
|
|
322385
322695
|
assistantRowRecovery: () => assistantRowRecovery,
|
|
@@ -322417,9 +322727,11 @@ __export(src_exports2, {
|
|
|
322417
322727
|
buildWorkorderMetrics: () => buildWorkorderMetrics,
|
|
322418
322728
|
buildWorkorderSummaries: () => buildWorkorderSummaries,
|
|
322419
322729
|
builtinSkillToFiles: () => builtinSkillToFiles,
|
|
322420
|
-
canAccessTodo: () => canAccessTodo,
|
|
322421
322730
|
canAnnounceRuntimeSwitch: () => canAnnounceRuntimeSwitch,
|
|
322731
|
+
canChangeTodo: () => canChangeTodo,
|
|
322422
322732
|
canRetryAllocation: () => canRetryAllocation,
|
|
322733
|
+
canSeeTodo: () => canSeeTodo,
|
|
322734
|
+
changedTitle: () => changedTitle,
|
|
322423
322735
|
chatBroadcastEnabledFromEnv: () => chatBroadcastEnabledFromEnv,
|
|
322424
322736
|
chatDelegationEnabledFromEnv: () => chatDelegationEnabledFromEnv,
|
|
322425
322737
|
chatDispatchScope: () => chatDispatchScope,
|
|
@@ -322794,6 +323106,7 @@ __export(src_exports2, {
|
|
|
322794
323106
|
stableChatTraceRunId: () => stableChatTraceRunId,
|
|
322795
323107
|
stableJson: () => stableJson2,
|
|
322796
323108
|
startOasisServer: () => startOasisServer,
|
|
323109
|
+
statusTitle: () => statusTitle,
|
|
322797
323110
|
stopDispatchesForDeletedActor: () => stopDispatchesForDeletedActor,
|
|
322798
323111
|
stripRetiredCdBotFromDrafts: () => stripRetiredCdBotFromDrafts,
|
|
322799
323112
|
subagentRunEventFromLive: () => subagentRunEventFromLive,
|
|
@@ -322811,7 +323124,10 @@ __export(src_exports2, {
|
|
|
322811
323124
|
toBundleFiles: () => toBundleFiles,
|
|
322812
323125
|
toChatDispatchRequest: () => toChatDispatchRequest,
|
|
322813
323126
|
toYamlDoubleQuoted: () => toYamlDoubleQuoted,
|
|
323127
|
+
todoAssignedSourceKey: () => todoAssignedSourceKey,
|
|
323128
|
+
todoChangeSourceKey: () => todoChangeSourceKey,
|
|
322814
323129
|
todoReminderSourceKey: () => todoReminderSourceKey,
|
|
323130
|
+
todoUnseenSourceKey: () => todoUnseenSourceKey,
|
|
322815
323131
|
todosDomain: () => todosDomain,
|
|
322816
323132
|
touchKey: () => touchKey,
|
|
322817
323133
|
traceDomain: () => traceDomain,
|
|
@@ -322820,6 +323136,7 @@ __export(src_exports2, {
|
|
|
322820
323136
|
trustedDelegationInvocationId: () => trustedDelegationInvocationId,
|
|
322821
323137
|
trustedKnowledgeInvocationId: () => trustedKnowledgeInvocationId,
|
|
322822
323138
|
typesDomain: () => typesDomain,
|
|
323139
|
+
unseenTitle: () => unseenTitle,
|
|
322823
323140
|
upgradeDefaultFieldDefs: () => upgradeDefaultFieldDefs,
|
|
322824
323141
|
validateCron: () => validateCron,
|
|
322825
323142
|
validateKnowledgeAnswer: () => validateKnowledgeAnswer,
|
|
@@ -325596,7 +325913,24 @@ function createTodoRuntime(options) {
|
|
|
325596
325913
|
const id = companyId ?? options.defaultCompanyId();
|
|
325597
325914
|
let value2 = cache5.get(id);
|
|
325598
325915
|
if (!value2) {
|
|
325599
|
-
value2 = options.store(id).then((store) => new TodoService({
|
|
325916
|
+
value2 = options.store(id).then((store) => new TodoService({
|
|
325917
|
+
store,
|
|
325918
|
+
timeZone: () => options.timeZone(id),
|
|
325919
|
+
...options.people ? { people: () => options.people(id) } : {},
|
|
325920
|
+
...options.runOf ? { runOf: options.runOf } : {},
|
|
325921
|
+
// 派给别人的待办的告知(ADR 0640):与到期提醒同一条路——直接 insert 进台账 `todo` 类,新写的推送 + 刷铃铛。
|
|
325922
|
+
notify: async (records) => {
|
|
325923
|
+
const ledger = await options.notifications(id);
|
|
325924
|
+
if (!ledger) return;
|
|
325925
|
+
const fresh = [];
|
|
325926
|
+
for (const r5 of records) {
|
|
325927
|
+
const row = await ledger.insert(r5);
|
|
325928
|
+
if (row.enteredAt === r5.enteredAt) fresh.push(row);
|
|
325929
|
+
}
|
|
325930
|
+
if (fresh.length > 0) options.onReminders?.(id, fresh);
|
|
325931
|
+
},
|
|
325932
|
+
warn: options.warn
|
|
325933
|
+
}));
|
|
325600
325934
|
cache5.set(id, value2);
|
|
325601
325935
|
void value2.catch(() => cache5.delete(id));
|
|
325602
325936
|
}
|
|
@@ -325614,6 +325948,7 @@ function createTodoRuntime(options) {
|
|
|
325614
325948
|
todos: options.store,
|
|
325615
325949
|
notifications: options.notifications,
|
|
325616
325950
|
timeZone: options.timeZone,
|
|
325951
|
+
...options.people ? { nameOf: async (companyId, actorId) => (await options.people(companyId)).find((p4) => p4.actorId === actorId)?.name ?? actorId } : {},
|
|
325617
325952
|
enabled,
|
|
325618
325953
|
warn: options.warn,
|
|
325619
325954
|
...options.onReminders ? { onInserted: options.onReminders } : {}
|
|
@@ -325962,6 +326297,7 @@ function createConnectorRenewalRuntime(options) {
|
|
|
325962
326297
|
init_store3();
|
|
325963
326298
|
|
|
325964
326299
|
// ../storage/src/postgres-todos.ts
|
|
326300
|
+
init_src();
|
|
325965
326301
|
init_pg_ident();
|
|
325966
326302
|
init_schema_ddl_lock();
|
|
325967
326303
|
var PostgresTodoStore = class _PostgresTodoStore {
|
|
@@ -325985,7 +326321,7 @@ var PostgresTodoStore = class _PostgresTodoStore {
|
|
|
325985
326321
|
due_time text,
|
|
325986
326322
|
start_date text,
|
|
325987
326323
|
start_time text,
|
|
325988
|
-
status text NOT NULL CHECK (status IN (
|
|
326324
|
+
status text NOT NULL CONSTRAINT todos_status_check CHECK (status IN (${STATUS_LIST})),
|
|
325989
326325
|
done_at timestamptz,
|
|
325990
326326
|
cancelled_at timestamptz,
|
|
325991
326327
|
source_chat_session_id text,
|
|
@@ -325993,6 +326329,17 @@ var PostgresTodoStore = class _PostgresTodoStore {
|
|
|
325993
326329
|
created_at timestamptz NOT NULL,
|
|
325994
326330
|
updated_at timestamptz NOT NULL
|
|
325995
326331
|
)`);
|
|
326332
|
+
await pool.query(`ALTER TABLE ${store.s}.todos ADD COLUMN IF NOT EXISTS sent_at timestamptz`);
|
|
326333
|
+
await pool.query(`ALTER TABLE ${store.s}.todos ADD COLUMN IF NOT EXISTS first_viewed_at timestamptz`);
|
|
326334
|
+
await pool.query(`ALTER TABLE ${store.s}.todos ADD COLUMN IF NOT EXISTS source_run_id text`);
|
|
326335
|
+
const check2 = await pool.query(`SELECT pg_get_constraintdef(c.oid) AS def FROM pg_constraint c
|
|
326336
|
+
JOIN pg_class t ON t.oid = c.conrelid JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
326337
|
+
WHERE n.nspname = $1 AND t.relname = 'todos' AND c.conname = 'todos_status_check'`, [schema]);
|
|
326338
|
+
const def = check2.rows[0]?.def;
|
|
326339
|
+
if (!def || !def.includes("'draft'")) {
|
|
326340
|
+
await pool.query(`ALTER TABLE ${store.s}.todos DROP CONSTRAINT IF EXISTS todos_status_check`);
|
|
326341
|
+
await pool.query(`ALTER TABLE ${store.s}.todos ADD CONSTRAINT todos_status_check CHECK (status IN (${STATUS_LIST}))`);
|
|
326342
|
+
}
|
|
325996
326343
|
await pool.query(`CREATE INDEX IF NOT EXISTS todos_assignee_status_due_idx ON ${store.s}.todos (assignee_actor_id, status, due_date)`);
|
|
325997
326344
|
await pool.query(`CREATE INDEX IF NOT EXISTS todos_creator_status_due_idx ON ${store.s}.todos (creator_actor_id, status, due_date)`);
|
|
325998
326345
|
await pool.query(`CREATE INDEX IF NOT EXISTS todos_open_due_idx ON ${store.s}.todos (due_date) WHERE status = 'open'`);
|
|
@@ -326038,8 +326385,9 @@ var PostgresTodoStore = class _PostgresTodoStore {
|
|
|
326038
326385
|
async create(todo, changes) {
|
|
326039
326386
|
return this.tx(async (client) => {
|
|
326040
326387
|
const r5 = await client.query(`INSERT INTO ${this.s}.todos (id,title,note,creator_actor_id,assignee_actor_id,recorded_by_actor_id,
|
|
326041
|
-
due_date,due_time,status,done_at,cancelled_at,source_chat_session_id,linked_workorder_id,created_at,updated_at,start_date,start_time
|
|
326042
|
-
|
|
326388
|
+
due_date,due_time,status,done_at,cancelled_at,source_chat_session_id,linked_workorder_id,created_at,updated_at,start_date,start_time,
|
|
326389
|
+
sent_at,first_viewed_at,source_run_id)
|
|
326390
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) RETURNING *`, rowValues(todo));
|
|
326043
326391
|
await this.insertChanges(client, changes);
|
|
326044
326392
|
return fromRow3(r5.rows[0]);
|
|
326045
326393
|
});
|
|
@@ -326052,8 +326400,9 @@ var PostgresTodoStore = class _PostgresTodoStore {
|
|
|
326052
326400
|
return this.tx(async (client) => {
|
|
326053
326401
|
const r5 = await client.query(`UPDATE ${this.s}.todos SET title=$2,note=$3,creator_actor_id=$4,assignee_actor_id=$5,
|
|
326054
326402
|
recorded_by_actor_id=$6,due_date=$7,due_time=$8,status=$9,done_at=$10,cancelled_at=$11,source_chat_session_id=$12,
|
|
326055
|
-
linked_workorder_id=$13,created_at=$14,updated_at=$15,start_date=$16,start_time=$17
|
|
326056
|
-
|
|
326403
|
+
linked_workorder_id=$13,created_at=$14,updated_at=$15,start_date=$16,start_time=$17,
|
|
326404
|
+
sent_at=$18,first_viewed_at=COALESCE(first_viewed_at,$19),source_run_id=$20
|
|
326405
|
+
WHERE id=$1 AND updated_at=$21::timestamptz RETURNING *`, [...rowValues(next), expectedUpdatedAt]);
|
|
326057
326406
|
if (!r5.rows[0]) return null;
|
|
326058
326407
|
await this.insertChanges(client, changes);
|
|
326059
326408
|
return fromRow3(r5.rows[0]);
|
|
@@ -326065,6 +326414,31 @@ var PostgresTodoStore = class _PostgresTodoStore {
|
|
|
326065
326414
|
ORDER BY due_date, COALESCE(due_time,'99:99'), created_at, id`, [actorId, [...statuses2]]);
|
|
326066
326415
|
return r5.rows.map(fromRow3);
|
|
326067
326416
|
}
|
|
326417
|
+
async search(f6) {
|
|
326418
|
+
const where = ["status = ANY($1::text[])"];
|
|
326419
|
+
const args = [[...f6.statuses]];
|
|
326420
|
+
const arg = (v2) => {
|
|
326421
|
+
args.push(v2);
|
|
326422
|
+
return `$${args.length}`;
|
|
326423
|
+
};
|
|
326424
|
+
if (f6.assignee) where.push(`assignee_actor_id = ${arg(f6.assignee)}`);
|
|
326425
|
+
if (f6.creator) where.push(`creator_actor_id = ${arg(f6.creator)}`);
|
|
326426
|
+
if (f6.dueFrom) where.push(`due_date >= ${arg(f6.dueFrom)}`);
|
|
326427
|
+
if (f6.dueTo) where.push(`due_date <= ${arg(f6.dueTo)}`);
|
|
326428
|
+
if (f6.chatSessionId) where.push(`source_chat_session_id = ${arg(f6.chatSessionId)}`);
|
|
326429
|
+
if (f6.q) {
|
|
326430
|
+
const like = arg(`%${f6.q.replace(/[\\%_]/g, (c5) => `\\${c5}`)}%`);
|
|
326431
|
+
where.push(`(title ILIKE ${like} OR COALESCE(note,'') ILIKE ${like})`);
|
|
326432
|
+
}
|
|
326433
|
+
where.push(`NOT (creator_actor_id <> assignee_actor_id AND sent_at IS NULL AND creator_actor_id <> ${arg(f6.viewer)})`);
|
|
326434
|
+
const r5 = await this.pool.query(`SELECT * FROM ${this.s}.todos WHERE ${where.join(" AND ")}
|
|
326435
|
+
ORDER BY due_date, COALESCE(due_time,'99:99'), created_at, id LIMIT ${arg(f6.limit)}`, args);
|
|
326436
|
+
return r5.rows.map(fromRow3);
|
|
326437
|
+
}
|
|
326438
|
+
async markFirstViewed(id, at2) {
|
|
326439
|
+
await this.pool.query(`UPDATE ${this.s}.todos SET first_viewed_at=$2 WHERE id=$1 AND first_viewed_at IS NULL`, [id, at2]);
|
|
326440
|
+
return this.get(id);
|
|
326441
|
+
}
|
|
326068
326442
|
async listOpenReminderCandidates(fromDay, toDay) {
|
|
326069
326443
|
const r5 = await this.pool.query(`SELECT * FROM ${this.s}.todos
|
|
326070
326444
|
WHERE status='open' AND (due_date BETWEEN $1 AND $2 OR start_date BETWEEN $1 AND $2)
|
|
@@ -326083,6 +326457,7 @@ var PostgresTodoStore = class _PostgresTodoStore {
|
|
|
326083
326457
|
});
|
|
326084
326458
|
}
|
|
326085
326459
|
};
|
|
326460
|
+
var STATUS_LIST = TODO_STATUSES.map((v2) => `'${v2}'`).join(",");
|
|
326086
326461
|
var iso8 = (v2) => v2 instanceof Date ? v2.toISOString() : typeof v2 === "string" ? new Date(v2).toISOString() : void 0;
|
|
326087
326462
|
function rowValues(t) {
|
|
326088
326463
|
return [
|
|
@@ -326102,7 +326477,10 @@ function rowValues(t) {
|
|
|
326102
326477
|
t.createdAt,
|
|
326103
326478
|
t.updatedAt,
|
|
326104
326479
|
t.startDate ?? null,
|
|
326105
|
-
t.startTime ?? null
|
|
326480
|
+
t.startTime ?? null,
|
|
326481
|
+
t.sentAt ?? null,
|
|
326482
|
+
t.firstViewedAt ?? null,
|
|
326483
|
+
t.sourceRunId ?? null
|
|
326106
326484
|
];
|
|
326107
326485
|
}
|
|
326108
326486
|
function fromRow3(row) {
|
|
@@ -326125,7 +326503,10 @@ function fromRow3(row) {
|
|
|
326125
326503
|
["doneAt", iso8(row["done_at"])],
|
|
326126
326504
|
["cancelledAt", iso8(row["cancelled_at"])],
|
|
326127
326505
|
["sourceChatSessionId", row["source_chat_session_id"]],
|
|
326128
|
-
["linkedWorkorderId", row["linked_workorder_id"]]
|
|
326506
|
+
["linkedWorkorderId", row["linked_workorder_id"]],
|
|
326507
|
+
["sentAt", iso8(row["sent_at"])],
|
|
326508
|
+
["firstViewedAt", iso8(row["first_viewed_at"])],
|
|
326509
|
+
["sourceRunId", row["source_run_id"]]
|
|
326129
326510
|
];
|
|
326130
326511
|
for (const [k6, v2] of opt) if (v2 !== void 0 && v2 !== null) t[k6] = v2;
|
|
326131
326512
|
return t;
|
|
@@ -329725,10 +330106,17 @@ async function startServe(opts) {
|
|
|
329725
330106
|
timeZone: (companyId) => companies.service.effectiveTimeZoneOf(companyId),
|
|
329726
330107
|
boundHuman: async (agentId, companyId) => (await (await resolveAssistantsCtx(companyId)).service.getByAgent(agentId))?.humanActorId ?? null,
|
|
329727
330108
|
notifications: (companyId) => notificationStoreFor(companyId),
|
|
330109
|
+
// 派给别人(ADR 0640):承接人只能是本公司走完新手指引的真人成员(与组织成员页同一份名单)。
|
|
330110
|
+
people: async (companyId) => (await companies.service.listMemberViews(companyId)).map((m4) => ({
|
|
330111
|
+
actorId: m4.actorId,
|
|
330112
|
+
name: m4.displayName?.trim() || m4.name?.trim() || m4.email || m4.actorId,
|
|
330113
|
+
aliases: [m4.name, m4.email, m4.email?.split("@")[0]].filter((v2) => !!v2 && !!v2.trim())
|
|
330114
|
+
})),
|
|
330115
|
+
runOf: (chatSessionId) => liveChat.runFor(chatSessionId),
|
|
329728
330116
|
onReminders: (companyId, records) => {
|
|
329729
330117
|
pushNotifier?.notify(companyId, records);
|
|
329730
330118
|
const recipients = [...new Set(records.map((r5) => r5.recipient))];
|
|
329731
|
-
console.warn(`[todos] ${companyId} \
|
|
330119
|
+
console.warn(`[todos] ${companyId} \u5F85\u529E\u901A\u77E5 ${records.length} \u6761 \u2192 ${recipients.join(",")}`);
|
|
329732
330120
|
auditSink.notifyNotificationsChanged(recipients);
|
|
329733
330121
|
},
|
|
329734
330122
|
defaultCompanyId: () => defaultCompanyId,
|
|
@@ -338999,16 +339387,19 @@ var COMMAND_DECLS = {
|
|
|
338999
339387
|
],
|
|
339000
339388
|
examples: ["oasis var rm STALE_KEY --scope project --project oasis", "oasis var rm --id var_1234 --yes"]
|
|
339001
339389
|
},
|
|
339002
|
-
// —— 个人待办(ADR 0635):人对助理说「帮我记一下」→
|
|
339003
|
-
// 填(agent 调 =
|
|
339390
|
+
// —— 个人待办(ADR 0635):人对助理说「帮我记一下」→ 助理用这组命令记、改、勾掉。发起人由服务端按调用者
|
|
339391
|
+
// 填(agent 调 = 它绑定的那个真人),命令行不收;派给别人用 --to(ADR 0640,先成草案、人点发出才生效)。
|
|
339392
|
+
// 开关 OASIS_PERSONAL_TODO=off 时服务端 404。 ——
|
|
339004
339393
|
todo: {
|
|
339005
339394
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339006
339395
|
usage: "oasis todo <add|list|show|edit|done|cancel|reopen> ...",
|
|
339007
|
-
description: "\
|
|
339396
|
+
description: "\u5F85\u529E\uFF1A\u66FF\u4F60\u7ED1\u5B9A\u7684\u90A3\u4E2A\u771F\u4EBA\u8BB0\u4E0B\u6709\u622A\u6B62\u65E5\u7684\u5DE5\u4F5C\uFF0C\u6216\u6D3E\u7ED9\u672C\u516C\u53F8\u7684\u540C\u4E8B\uFF08--to\uFF0C\u5148\u6210\u8349\u6848\u3001TA \u5728\u5361\u4E0A\u70B9\u300C\u53D1\u51FA\u300D\u624D\u751F\u6548\uFF09\uFF1B\u5230\u671F\u524D\u4E00\u5929 09:00\u3001\u5F53\u5929 09:00 \u7CFB\u7EDF\u4F1A\u63D0\u9192\u627F\u63A5\u4EBA\uFF08\u8BBE\u4E86\u5F00\u59CB\u65E5\u7684\uFF0C\u5F00\u59CB\u5F53\u5929 09:00 \u4E5F\u63D0\u9192\u4E00\u6B21\uFF09\u3002\u5168\u516C\u53F8\u7684\u5F85\u529E\u90FD\u80FD\u67E5\uFF08list --assignee / --creator / --everyone / --q\uFF09\u3002\u6BCF\u6761\u8F93\u51FA\u7B2C\u4E00\u884C\u662F\u300C\u4ECA\u5929\uFF1AYYYY-MM-DD \u5468X\uFF08\u65F6\u533A\uFF09\u300D\u2014\u2014\u6362\u7B97\u300C\u8FD9\u5468\u4E94\u300D\u300C\u6708\u5E95\u300D\u4E00\u5F8B\u4EE5\u5B83\u4E3A\u51C6\u3002**\u6CA1\u6709 `oasis todo` \u547D\u4EE4\u6267\u884C\u6210\u529F\uFF0C\u5C31\u4E0D\u80FD\u8BF4\u8BB0\u4E0B\u4E86\u3002**",
|
|
339008
339397
|
subCommands: ["todo add", "todo list", "todo show", "todo edit", "todo done", "todo cancel", "todo reopen"],
|
|
339009
339398
|
examples: [
|
|
339010
339399
|
'oasis todo add --title "\u4EA4 Q3 \u590D\u76D8\u62A5\u544A" --note "\u738B\u603B\u8981\uFF0C\u5468\u4F1A\u524D\u8FC7\u4E00\u904D" --due 2026-10-09',
|
|
339400
|
+
'oasis todo add --to \u5955\u7487 --title "\u6574\u7406\u7ADE\u54C1\u62A5\u4EF7" --due 2026-10-15',
|
|
339011
339401
|
"oasis todo list",
|
|
339402
|
+
"oasis todo list --assignee \u5955\u7487",
|
|
339012
339403
|
"oasis todo show td_3f9a1c2b",
|
|
339013
339404
|
'oasis todo edit td_3f9a1c2b --due 2026-10-12 --reason "\u6392\u671F\u8C03\u6574"',
|
|
339014
339405
|
"oasis todo done td_3f9a1c2b"
|
|
@@ -339016,9 +339407,10 @@ var COMMAND_DECLS = {
|
|
|
339016
339407
|
},
|
|
339017
339408
|
"todo add": {
|
|
339018
339409
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339019
|
-
usage: "oasis todo add --title <\u4E00\u53E5\u8BDD\uFF0C\u52A8\u8BCD\u5F00\u5934> [--note <\u8C01\u8981\u3001\u4E3A\u4EC0\u4E48\u3001\u539F\u8BDD\u8981\u70B9>] [--start YYYY-MM-DD [--start-time HH:mm]] --due YYYY-MM-DD [--time HH:mm]",
|
|
339020
|
-
description: "\u8BB0\u4E00\u6761\u5F85\u529E\u3002--due / --start \u53EA\u6536\u4E0D\u5E26\u65F6\u533A\u7684\u65E5\u671F\uFF08\u7EC4\u7EC7\u65F6\u533A\u91CC\u7684\u90A3\u4E00\u5929\uFF09\uFF0C\u975E\u6CD5\u65E5\u671F\u5F53\u573A\u62A5\u9519\uFF1B\u6CA1\u8BF4\u51E0\u70B9\u5C31\u522B\u7ED9 --time\uFF1B\u7528\u6237\u6CA1\u8BF4\u4EC0\u4E48\u65F6\u5019\u5F00\u59CB\u5C31\u522B\u7ED9 --start\uFF08\u4E0D\u8FFD\u95EE\u3001\u4E0D\u731C\uFF09\uFF0C\u5F00\u59CB\u4E0D\u80FD\u665A\u4E8E\u622A\u6B62\u3002\u6765\u6E90\u5BF9\u8BDD\u81EA\u52A8\u53D6 $OASIS_CHAT_SESSION_ID\uFF08\u63D0\u9192\u70B9\u5F00\u4F1A\u8DF3\u56DE\u8FD9\u91CC\uFF09\u3002\u6210\u529F\u8F93\u51FA\u300C\u8BB0\u4E0B\u4E86\uFF1A<\u6807\u9898>\uFF0C\u622A\u6B62 X \u6708 X \u65E5\uFF08\u5468X\uFF09\u300D\u3002",
|
|
339410
|
+
usage: "oasis todo add [--to <\u4EBA>] --title <\u4E00\u53E5\u8BDD\uFF0C\u52A8\u8BCD\u5F00\u5934> [--note <\u8C01\u8981\u3001\u4E3A\u4EC0\u4E48\u3001\u539F\u8BDD\u8981\u70B9>] [--start YYYY-MM-DD [--start-time HH:mm]] --due YYYY-MM-DD [--time HH:mm]",
|
|
339411
|
+
description: "\u8BB0\u4E00\u6761\u5F85\u529E\u3002--due / --start \u53EA\u6536\u4E0D\u5E26\u65F6\u533A\u7684\u65E5\u671F\uFF08\u7EC4\u7EC7\u65F6\u533A\u91CC\u7684\u90A3\u4E00\u5929\uFF09\uFF0C\u975E\u6CD5\u65E5\u671F\u5F53\u573A\u62A5\u9519\uFF1B\u6CA1\u8BF4\u51E0\u70B9\u5C31\u522B\u7ED9 --time\uFF1B\u7528\u6237\u6CA1\u8BF4\u4EC0\u4E48\u65F6\u5019\u5F00\u59CB\u5C31\u522B\u7ED9 --start\uFF08\u4E0D\u8FFD\u95EE\u3001\u4E0D\u731C\uFF09\uFF0C\u5F00\u59CB\u4E0D\u80FD\u665A\u4E8E\u622A\u6B62\u3002\u6765\u6E90\u5BF9\u8BDD\u81EA\u52A8\u53D6 $OASIS_CHAT_SESSION_ID\uFF08\u63D0\u9192\u70B9\u5F00\u4F1A\u8DF3\u56DE\u8FD9\u91CC\uFF09\u3002\u7ED9\u81EA\u5DF1\u8BB0\uFF1A\u6210\u529F\u8F93\u51FA\u300C\u8BB0\u4E0B\u4E86\uFF1A<\u6807\u9898>\uFF0C\u622A\u6B62 X \u6708 X \u65E5\uFF08\u5468X\uFF09\u300D\u3002--to \u6D3E\u7ED9\u522B\u4EBA\uFF1A\u53EA\u5B58\u6210**\u8349\u6848**\uFF0C\u8F93\u51FA\u300C\u8349\u6848\uFF08\u8FD8\u6CA1\u53D1\u51FA\uFF09\uFF1A\u6D3E\u7ED9 X\uFF1A\u2026\u300D\uFF0C\u5BF9\u8BDD\u91CC\u51FA\u4E00\u5F20\u8349\u6848\u5361\uFF0C\u7528\u6237\u70B9\u300C\u53D1\u51FA\u300D\u624D\u751F\u6548\u3001\u5BF9\u65B9\u624D\u6536\u5230\u901A\u77E5\u2014\u2014\u4F60\u4E0D\u80FD\u66FF TA \u53D1\u51FA\u3002--to \u8BA4 actorId \u6216\u540D\u5B57\uFF0C\u5BF9\u4E0D\u4E0A\u6216\u5BF9\u4E0A\u4E0D\u6B62\u4E00\u4E2A\u76F4\u63A5\u62A5\u9519\uFF0C\u522B\u731C\uFF0C\u95EE\u7528\u6237\u662F\u54EA\u4F4D\u3002",
|
|
339021
339412
|
flags: [
|
|
339413
|
+
{ name: "to", desc: "\u6D3E\u7ED9\u8C01\uFF1A\u672C\u516C\u53F8\u771F\u4EBA\u7684 actorId \u6216\u540D\u5B57\uFF08\u4E0D\u7ED9 = \u7ED9\u81EA\u5DF1\u8BB0\uFF09", own: true },
|
|
339022
339414
|
{ name: "title", desc: "\u6807\u9898\uFF1A\u4E00\u53E5\u8BDD\u3001\u52A8\u8BCD\u5F00\u5934", required: true, own: true },
|
|
339023
339415
|
{ name: "note", desc: "\u5907\u6CE8\uFF1A\u8C01\u8981\u3001\u4E3A\u4EC0\u4E48\u8981\u3001\u539F\u8BDD\u91CC\u7684\u5173\u952E\u4FE1\u606F", own: true },
|
|
339024
339416
|
{ name: "due", desc: "\u622A\u6B62\u65E5 YYYY-MM-DD\uFF08\u4E0D\u5E26\u65F6\u533A\uFF09", required: true, own: true },
|
|
@@ -339026,25 +339418,35 @@ var COMMAND_DECLS = {
|
|
|
339026
339418
|
{ name: "start", desc: "\u5F00\u59CB\u65E5 YYYY-MM-DD\uFF08\u7528\u6237\u8BF4\u4E86\u300C\u54EA\u5929\u5F00\u59CB\u5F04\u300D\u624D\u7ED9\uFF09", own: true },
|
|
339027
339419
|
{ name: "start-time", desc: "\u5F00\u59CB\u65F6\u523B HH:mm\uFF08\u914D --start\uFF09", own: true }
|
|
339028
339420
|
],
|
|
339029
|
-
examples: ['oasis todo add --title "\u4EA4 Q3 \u590D\u76D8\u62A5\u544A" --due 2026-10-09 --time 18:00']
|
|
339421
|
+
examples: ['oasis todo add --title "\u4EA4 Q3 \u590D\u76D8\u62A5\u544A" --due 2026-10-09 --time 18:00', 'oasis todo add --to \u5955\u7487 --title "\u6574\u7406\u7ADE\u54C1\u62A5\u4EF7" --due 2026-10-15']
|
|
339030
339422
|
},
|
|
339031
339423
|
"todo list": {
|
|
339032
339424
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339033
|
-
usage: "oasis todo list [--all]",
|
|
339034
|
-
description: "\u5217\u51FA\u5F85\u529E\uFF0C\u6309\u622A\u6B62\u65E5\u6392\u5E8F\uFF0C\u8FC7\u671F\u7684\u6807\u300C\u5DF2\u8FC7\u671F\u300D\uFF1B\u8BBE\u4E86\u5F00\u59CB\u65E5\u7684\u663E\u793A\u300CX \u6708 X \u65E5\u5F00\u59CB \u2192 X \u6708 X \u65E5\u622A\u6B62\u300D\u3002\
|
|
339035
|
-
flags: [
|
|
339425
|
+
usage: "oasis todo list [--assignee <\u4EBA>] [--creator <\u4EBA>] [--everyone] [--q <\u5173\u952E\u8BCD>] [--due-from YYYY-MM-DD] [--due-to YYYY-MM-DD] [--status <s,\u2026>] [--all]",
|
|
339426
|
+
description: "\u5217\u51FA\u5F85\u529E\uFF0C\u6309\u622A\u6B62\u65E5\u6392\u5E8F\uFF0C\u6BCF\u884C\u5E26\u627F\u63A5\u4EBA\u548C\u53D1\u8D77\u4EBA\uFF0C\u8FC7\u671F\u7684\u6807\u300C\u5DF2\u8FC7\u671F\u300D\u3001\u6CA1\u53D1\u51FA\u7684\u8349\u6848\u6807\u300C\u8349\u6848\xB7\u672A\u53D1\u51FA\u300D\uFF1B\u8BBE\u4E86\u5F00\u59CB\u65E5\u7684\u663E\u793A\u300CX \u6708 X \u65E5\u5F00\u59CB \u2192 X \u6708 X \u65E5\u622A\u6B62\u300D\u3002\u4EC0\u4E48\u90FD\u4E0D\u7ED9 = \u6211\u627F\u63A5\u7684\u3001\u672A\u5B8C\u6210\u7684\u3002\u5168\u516C\u53F8\u7684\u5F85\u529E\u90FD\u80FD\u67E5\uFF1A--assignee / --creator \u6309\u4EBA\uFF08actorId\u3001\u540D\u5B57\u6216 me\uFF09\uFF0C--everyone \u4E0D\u6309\u4EBA\uFF0C--q \u641C\u6807\u9898\u548C\u5907\u6CE8\uFF0C--due-from / --due-to \u622A\u6B62\u65E5\u8303\u56F4\uFF08\u542B\u4E24\u7AEF\uFF09\u3002\u522B\u4EBA\u7684\u8349\u6848\u6C38\u8FDC\u67E5\u4E0D\u5230\u3002",
|
|
339427
|
+
flags: [
|
|
339428
|
+
{ name: "assignee", desc: "\u627F\u63A5\u4EBA\uFF08actorId\u3001\u540D\u5B57\u6216 me\uFF09", own: true },
|
|
339429
|
+
{ name: "creator", desc: "\u53D1\u8D77\u4EBA\uFF08actorId\u3001\u540D\u5B57\u6216 me\uFF09", own: true },
|
|
339430
|
+
{ name: "everyone", desc: "\u4E0D\u6309\u4EBA\u7B5B\uFF0C\u5168\u516C\u53F8", boolean: true, own: true },
|
|
339431
|
+
{ name: "q", desc: "\u5173\u952E\u8BCD\uFF1A\u6807\u9898\u6216\u5907\u6CE8\u91CC\u5305\u542B", own: true },
|
|
339432
|
+
{ name: "due-from", desc: "\u622A\u6B62\u65E5\u4E0D\u65E9\u4E8E YYYY-MM-DD", own: true },
|
|
339433
|
+
{ name: "due-to", desc: "\u622A\u6B62\u65E5\u4E0D\u665A\u4E8E YYYY-MM-DD", own: true },
|
|
339434
|
+
{ name: "status", desc: "\u72B6\u6001\uFF0C\u53EF\u591A\u9009\u9017\u53F7\u5206\u9694\uFF1Adraft / open / done / cancelled\uFF08\u9ED8\u8BA4 open\uFF09", own: true },
|
|
339435
|
+
{ name: "all", desc: "\u56DB\u79CD\u72B6\u6001\u90FD\u5217\uFF08\u542B\u5DF2\u5B8C\u6210 / \u5DF2\u53D6\u6D88\uFF1B\u8349\u6848\u53EA\u5217\u81EA\u5DF1\u53D1\u8D77\u7684\uFF09", boolean: true, own: true }
|
|
339436
|
+
],
|
|
339437
|
+
examples: ["oasis todo list --assignee \u5955\u7487", "oasis todo list --everyone --due-from 2026-10-12 --due-to 2026-10-18", "oasis todo list --everyone --q \u62A5\u4EF7 --all", "oasis todo list --creator me"]
|
|
339036
339438
|
},
|
|
339037
339439
|
"todo show": {
|
|
339038
339440
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339039
339441
|
usage: "oasis todo show <id>",
|
|
339040
|
-
description: "\u770B\u4E00\u6761\u5F85\u529E\u7684\u5168\u8C8C\uFF1A\u6807\u9898\u3001\u5F00\u59CB \u2192 \u622A\u6B62\u3001\u72B6\u6001\u3001\u5907\u6CE8\u5168\u6587\uFF0C\u4EE5\u53CA\u53D8\u66F4\u8BB0\u5F55\uFF08\u65F6\u95F4\u3001\u8C01\u6539\u7684 / \u7ECF\u624B agent\u3001\u5B57\u6BB5\u3001\u65E7\u503C \u2192 \u65B0\u503C\u3001\u539F\u56E0\uFF0C\u65B0\u7684\u5728\u524D\uFF09\u3002\u6539\u5907\u6CE8\u524D\u5148\u7528\u5B83\u8BFB\u51FA\u5F53\u524D\u5907\u6CE8\u5168\u6587\uFF1B\u7528\u6237\u95EE\u300C\u4E4B\u524D\u5199\u7684\u662F\u4EC0\u4E48\u300D\u4E5F\u67E5\u8FD9\u91CC\u3002",
|
|
339442
|
+
description: "\u770B\u4E00\u6761\u5F85\u529E\u7684\u5168\u8C8C\uFF1A\u6807\u9898\u3001\u5F00\u59CB \u2192 \u622A\u6B62\u3001\u72B6\u6001\u3001\u53D1\u8D77\u4EBA\u3001\u627F\u63A5\u4EBA\uFF08\u6D3E\u7ED9\u522B\u4EBA\u7684\u8FD8\u6709\u53D1\u51FA\u65F6\u95F4\u3001\u627F\u63A5\u4EBA\u9996\u6B21\u67E5\u770B\u65F6\u95F4\uFF09\u3001\u5907\u6CE8\u5168\u6587\uFF0C\u4EE5\u53CA\u53D8\u66F4\u8BB0\u5F55\uFF08\u65F6\u95F4\u3001\u8C01\u6539\u7684 / \u7ECF\u624B agent\u3001\u5B57\u6BB5\u3001\u65E7\u503C \u2192 \u65B0\u503C\u3001\u539F\u56E0\uFF0C\u65B0\u7684\u5728\u524D\uFF09\u3002\u6539\u5907\u6CE8\u524D\u5148\u7528\u5B83\u8BFB\u51FA\u5F53\u524D\u5907\u6CE8\u5168\u6587\uFF1B\u7528\u6237\u95EE\u300C\u4E4B\u524D\u5199\u7684\u662F\u4EC0\u4E48\u300D\u4E5F\u67E5\u8FD9\u91CC\u3002\u672C\u516C\u53F8\u8C01\u7684\u5F85\u529E\u90FD\u80FD\u770B\uFF08\u522B\u4EBA\u7684\u8349\u6848\u9664\u5916\uFF09\uFF1B\u627F\u63A5\u4EBA\u7B2C\u4E00\u6B21\u770B\u4F1A\u88AB\u8BB0\u4E0B\u3002",
|
|
339041
339443
|
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id\uFF08oasis todo list \u7B2C\u4E00\u5217\uFF09" }],
|
|
339042
339444
|
examples: ["oasis todo show td_3f9a1c2b"]
|
|
339043
339445
|
},
|
|
339044
339446
|
"todo edit": {
|
|
339045
339447
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339046
|
-
usage: "oasis todo edit <id> [--title \u2026] [--note \u2026] [--start YYYY-MM-DD|none] [--start-time HH:mm|none] [--due YYYY-MM-DD] [--time HH:mm|none] [--workorder <\u5DE5\u5355 id>|none] [--reason \u2026]",
|
|
339047
|
-
description: '\u6539\u4E00\u6761\u5F85\u529E\u7684\u5B57\u6BB5\uFF0C\u7ED9\u4E86\u54EA\u4E2A\u6539\u54EA\u4E2A\uFF1B\u6BCF\u6539\u4E00\u4E2A\u5B57\u6BB5\u90FD\u7559\u4E00\u6761\u8BB0\u5F55\uFF08\u65E7\u503C\u3001\u65B0\u503C\u3001\u539F\u56E0\uFF09\u3002\u6539\u671F\u8981\u5199\u539F\u56E0\uFF1A\u7528\u6237\u6CA1\u8BF4\u5C31\u95EE\u4E00\u53E5\uFF0C\u7528\u6237\u8BF4\u4E0D\u7528\u5C31\u4E0D\u5199\uFF0C\u522B\u7F16\u3002--start none\uFF08\u8FDE\u540C\u5F00\u59CB\u65F6\u523B\uFF09/ --start-time none / --time none / --workorder none \u6E05\u7A7A\uFF0C--note "" \u6E05\u7A7A\u5907\u6CE8\u3002',
|
|
339448
|
+
usage: "oasis todo edit <id> [--title \u2026] [--note \u2026] [--start YYYY-MM-DD|none] [--start-time HH:mm|none] [--due YYYY-MM-DD] [--time HH:mm|none] [--workorder <\u5DE5\u5355 id>|none] [--to <\u4EBA>] [--reason \u2026]",
|
|
339449
|
+
description: '\u6539\u4E00\u6761\u5F85\u529E\u7684\u5B57\u6BB5\uFF0C\u7ED9\u4E86\u54EA\u4E2A\u6539\u54EA\u4E2A\uFF1B\u6BCF\u6539\u4E00\u4E2A\u5B57\u6BB5\u90FD\u7559\u4E00\u6761\u8BB0\u5F55\uFF08\u65E7\u503C\u3001\u65B0\u503C\u3001\u539F\u56E0\uFF09\u3002\u53EA\u6709\u53D1\u8D77\u4EBA\u548C\u627F\u63A5\u4EBA\u80FD\u6539\uFF1B\u6D3E\u7ED9\u522B\u4EBA\u4E14\u5DF2\u53D1\u51FA\u7684\uFF0C\u6539\u4E86\u4F1A\u901A\u77E5\u53E6\u4E00\u65B9\u3002\u6539\u671F\u8981\u5199\u539F\u56E0\uFF1A\u7528\u6237\u6CA1\u8BF4\u5C31\u95EE\u4E00\u53E5\uFF1B**\u522B\u4EBA\u6D3E\u7ED9\u4F60\u7684\u6539\u622A\u6B62\u5FC5\u987B\u5E26 --reason**\uFF08\u4E0D\u5E26\u76F4\u63A5\u62A5\u9519\uFF09\uFF0C\u81EA\u5DF1\u8BB0\u7684\u7528\u6237\u8BF4\u4E0D\u7528\u5C31\u4E0D\u5199\uFF0C\u522B\u7F16\u3002--to \u53EA\u80FD\u5728\u8FD8\u6CA1\u53D1\u51FA\u7684\u8349\u6848\u4E0A\u6362\u627F\u63A5\u4EBA\u3002--start none\uFF08\u8FDE\u540C\u5F00\u59CB\u65F6\u523B\uFF09/ --start-time none / --time none / --workorder none \u6E05\u7A7A\uFF0C--note "" \u6E05\u7A7A\u5907\u6CE8\u3002',
|
|
339048
339450
|
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id\uFF08oasis todo list \u7B2C\u4E00\u5217\uFF09" }],
|
|
339049
339451
|
flags: [
|
|
339050
339452
|
{ name: "title", desc: "\u65B0\u6807\u9898", own: true },
|
|
@@ -339054,28 +339456,31 @@ var COMMAND_DECLS = {
|
|
|
339054
339456
|
{ name: "due", desc: "\u65B0\u622A\u6B62\u65E5 YYYY-MM-DD", own: true },
|
|
339055
339457
|
{ name: "time", desc: "\u65B0\u622A\u6B62\u65F6\u523B HH:mm\uFF1Bnone = \u6E05\u7A7A", own: true },
|
|
339056
339458
|
{ name: "workorder", desc: "\u5173\u8054\u7684\u5DE5\u5355 id\uFF08\u300C\u8FD9\u4EF6\u4F60\u5E2E\u6211\u505A\u4E86\u5427\u300D\u5EFA\u5355\u540E\u6302\u4E0A\uFF09\uFF1Bnone = \u6E05\u7A7A", own: true },
|
|
339057
|
-
{ name: "
|
|
339459
|
+
{ name: "to", desc: "\u6362\u627F\u63A5\u4EBA\uFF08\u53EA\u9650\u8FD8\u6CA1\u53D1\u51FA\u7684\u8349\u6848\uFF09", own: true },
|
|
339460
|
+
{ name: "reason", desc: "\u4E3A\u4EC0\u4E48\u6539\uFF08\u6539\u671F\u65F6\u8981\u5199\uFF1B\u522B\u4EBA\u6D3E\u7ED9\u4F60\u7684\u6539\u622A\u6B62\u5FC5\u987B\u5199\uFF09", own: true }
|
|
339058
339461
|
],
|
|
339059
339462
|
examples: ['oasis todo edit td_3f9a1c2b --due 2026-10-12 --reason "\u5BA2\u6237\u5EF6\u671F\u4E00\u5468"']
|
|
339060
339463
|
},
|
|
339061
339464
|
"todo done": {
|
|
339062
339465
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339063
|
-
usage: "oasis todo done <id>",
|
|
339064
|
-
description: "\u6807\u8BB0\u5B8C\u6210\u3002\u5339\u914D\u5230\u591A\u6761\u65F6\u5148\u95EE\u7528\u6237\u662F\u54EA\u6761\uFF0C\u522B\u731C\u3002",
|
|
339065
|
-
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }]
|
|
339466
|
+
usage: "oasis todo done <id> [--reason \u2026]",
|
|
339467
|
+
description: "\u6807\u8BB0\u5B8C\u6210\u3002\u5339\u914D\u5230\u591A\u6761\u65F6\u5148\u95EE\u7528\u6237\u662F\u54EA\u6761\uFF0C\u522B\u731C\u3002\u6D3E\u7ED9\u522B\u4EBA\u4E14\u5DF2\u53D1\u51FA\u7684\uFF0C\u5B8C\u6210\u540E\u901A\u77E5\u53E6\u4E00\u65B9\u3002",
|
|
339468
|
+
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }],
|
|
339469
|
+
flags: [{ name: "reason", desc: "\u5907\u6CE8\u4E00\u53E5\uFF08\u53EF\u9009\uFF09", own: true }]
|
|
339066
339470
|
},
|
|
339067
339471
|
"todo cancel": {
|
|
339068
339472
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339069
339473
|
usage: "oasis todo cancel <id> [--reason \u2026]",
|
|
339070
|
-
description: "\u53D6\u6D88\uFF08\u4E0D\u7528\u505A\u4E86\uFF09\u3002\u53EA\u6539\u72B6\u6001\u3001\u4E0D\u5220\u9664\uFF0C--all \u5217\u8868\u91CC\u8FD8\u770B\u5F97\u5230\u3002",
|
|
339474
|
+
description: "\u53D6\u6D88\uFF08\u4E0D\u7528\u505A\u4E86\uFF09\u3002\u53EA\u6539\u72B6\u6001\u3001\u4E0D\u5220\u9664\uFF08\u5F85\u529E\u6C38\u4E0D\u5220\u9664\uFF09\uFF0C--all \u5217\u8868\u548C show \u91CC\u90FD\u8FD8\u770B\u5F97\u5230\u3002\u53D1\u8D77\u4EBA\u3001\u627F\u63A5\u4EBA\u90FD\u80FD\u53D6\u6D88\uFF0C\u6D3E\u7ED9\u522B\u4EBA\u4E14\u5DF2\u53D1\u51FA\u7684\u4F1A\u901A\u77E5\u53E6\u4E00\u65B9\uFF1B**\u522B\u4EBA\u6D3E\u7ED9\u4F60\u7684\u53D6\u6D88\u5FC5\u987B\u5E26 --reason**\u3002",
|
|
339071
339475
|
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }],
|
|
339072
|
-
flags: [{ name: "reason", desc: "\u4E3A\u4EC0\u4E48\u4E0D\u7528\u505A\u4E86", own: true }]
|
|
339476
|
+
flags: [{ name: "reason", desc: "\u4E3A\u4EC0\u4E48\u4E0D\u7528\u505A\u4E86\uFF08\u522B\u4EBA\u6D3E\u7ED9\u4F60\u7684\u5FC5\u987B\u5199\uFF09", own: true }]
|
|
339073
339477
|
},
|
|
339074
339478
|
"todo reopen": {
|
|
339075
339479
|
group: "\u4E2A\u4EBA\u5F85\u529E",
|
|
339076
|
-
usage: "oasis todo reopen <id>",
|
|
339077
|
-
description: "\u628A\u5DF2\u5B8C\u6210 / \u5DF2\u53D6\u6D88\u7684\u5F85\u529E\u91CD\u65B0\u6253\u5F00\u3002",
|
|
339078
|
-
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }]
|
|
339480
|
+
usage: "oasis todo reopen <id> [--reason \u2026]",
|
|
339481
|
+
description: "\u628A\u5DF2\u5B8C\u6210 / \u5DF2\u53D6\u6D88\u7684\u5F85\u529E\u91CD\u65B0\u6253\u5F00\uFF08\u53D1\u8D77\u4EBA\u89C9\u5F97\u6CA1\u505A\u5B8C\u4E5F\u7528\u5B83\uFF09\u3002\u6D3E\u7ED9\u522B\u4EBA\u4E14\u5DF2\u53D1\u51FA\u7684\uFF0C\u4F1A\u901A\u77E5\u53E6\u4E00\u65B9\u3002\u8349\u6848\u53D6\u6D88\u540E\u91CD\u5F00\u4ECD\u662F\u8349\u6848\uFF0C\u4E0D\u4F1A\u56E0\u6B64\u53D1\u51FA\u3002",
|
|
339482
|
+
positional: [{ name: "<id>", required: true, desc: "\u5F85\u529E id" }],
|
|
339483
|
+
flags: [{ name: "reason", desc: "\u4E3A\u4EC0\u4E48\u91CD\u5F00\uFF08\u53EF\u9009\uFF09", own: true }]
|
|
339079
339484
|
},
|
|
339080
339485
|
// —— 技能:组织技能库的 CLI 面(Web 上是「管理 > 技能」那一页)。
|
|
339081
339486
|
// agent 把自己写好的一个技能目录沉淀进公司,并决定它对谁生效。 ——
|
|
@@ -339402,7 +339807,7 @@ function formatProjectDecisions(projectId2, view, query) {
|
|
|
339402
339807
|
"\u5DF2\u786E\u8BA4 = \u5B9A\u4E86\u8981\u8FD9\u4E48\u505A\uFF0C\u4E0D\u4EE3\u8868\u5DF2\u7ECF\u4E0A\u7EBF\uFF1B\u6807\u300C\u5DF2\u4E0A\u7EBF\u300D\u7684\u624D\u662F\u73B0\u5728\u7CFB\u7EDF\u91CC\u7684\u6837\u5B50\u3002",
|
|
339403
339808
|
""
|
|
339404
339809
|
];
|
|
339405
|
-
let
|
|
339810
|
+
let shown2 = 0;
|
|
339406
339811
|
for (const t of topics) {
|
|
339407
339812
|
const rows = view.decisions.filter((d5) => d5.value.topicId === t.value.topicId).filter((d5) => {
|
|
339408
339813
|
if (state2 ? !stateMatches(d5.value, state2, query.now) : !["accepted", "proposed"].includes(d5.value.record.state)) return false;
|
|
@@ -339416,7 +339821,7 @@ function formatProjectDecisions(projectId2, view, query) {
|
|
|
339416
339821
|
if (!rows.length) lines.push("\uFF08\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u51B3\u7B56\uFF09");
|
|
339417
339822
|
for (const d5 of rows) {
|
|
339418
339823
|
const r5 = d5.value.record;
|
|
339419
|
-
|
|
339824
|
+
shown2 += 1;
|
|
339420
339825
|
lines.push(`- [${label(d5.value, query.now)}] ${r5.statement} \uFF08\u66F4\u65B0 ${r5.updated_at.slice(0, 10)}\uFF0C${d5.value.decisionId}\uFF09`);
|
|
339421
339826
|
if (!detail) continue;
|
|
339422
339827
|
lines.push(` \u539F\u56E0\uFF1A${r5.rationale || "\u672A\u8BF4\u660E"}`, ` \u786E\u8BA4\u4EBA\uFF1A${r5.confirmer ?? "\u672A\u786E\u8BA4"}`);
|
|
@@ -339427,7 +339832,7 @@ function formatProjectDecisions(projectId2, view, query) {
|
|
|
339427
339832
|
}
|
|
339428
339833
|
lines.push("");
|
|
339429
339834
|
}
|
|
339430
|
-
if (!
|
|
339835
|
+
if (!shown2 && !detail) lines.push("\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u51B3\u7B56\u3002");
|
|
339431
339836
|
return lines;
|
|
339432
339837
|
}
|
|
339433
339838
|
function writeContextSnapshot(root5, snapshot) {
|
|
@@ -339827,10 +340232,10 @@ function renderGraphLines(graph) {
|
|
|
339827
340232
|
const systemNote = closureIds.has(n3.id) ? " [\u672C\u5355\u672B\u7AEF\xB7\u7CFB\u7EDF\u7EF4\u62A4\uFF0C\u8FDE\u7EBF\u4E0E\u4EFB\u52A1\u4E66\u90FD\u4E0D\u7528\u4F60\u7BA1]" : "";
|
|
339828
340233
|
out.push(` type=${n3.type}${n3.stage ? ` \u9636\u6BB5=${n3.stage}` : ""}${n3.blocked ? " [\u5361\u4F4F]" : ""}${n3.unresolvedEscalations ? ` [\u4E0A\u62A5 ${n3.unresolvedEscalations}]` : ""} owner=${n3.owner?.name ?? n3.owner?.id ?? "?"}${n3.label ? ` ${n3.label}` : ""}${systemNote}`);
|
|
339829
340234
|
}
|
|
339830
|
-
const
|
|
340235
|
+
const shown2 = graph.edges.filter((e5) => !closureIds.has(e5.from));
|
|
339831
340236
|
const folded = [...closureIds].filter((id) => graph.edges.some((e5) => e5.from === id));
|
|
339832
|
-
out.push(
|
|
339833
|
-
for (const e5 of
|
|
340237
|
+
out.push(shown2.length > 0 || folded.length > 0 ? "\n\u4F9D\u8D56\u8FB9\uFF08\u6D88\u8D39\u8005 \u2192 \u4E0A\u6E38\uFF09:" : "\n\u4F9D\u8D56\u8FB9\uFF1A\uFF08\u65E0\uFF09");
|
|
340238
|
+
for (const e5 of shown2) out.push(` ${e5.from} \u2192 ${e5.to}${e5.required === false ? "\uFF08\u53EF\u9009\uFF09" : ""}`);
|
|
339834
340239
|
for (const id of folded) out.push(` ${id} \u2192 \uFF08\u672C\u5355\u5168\u90E8\u4E1A\u52A1\u4EA7\u7269\xB7\u7CFB\u7EDF\u7EF4\u62A4\uFF09`);
|
|
339835
340240
|
return out;
|
|
339836
340241
|
}
|
|
@@ -339931,7 +340336,7 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback2) {
|
|
|
339931
340336
|
}
|
|
339932
340337
|
}
|
|
339933
340338
|
}
|
|
339934
|
-
var CLI_VERSION = "2.2.
|
|
340339
|
+
var CLI_VERSION = "2.2.122" ? "2.2.122" : "dev";
|
|
339935
340340
|
function apiHeaders(token2, company) {
|
|
339936
340341
|
const headers = {
|
|
339937
340342
|
authorization: `Bearer ${token2}`,
|
|
@@ -342328,6 +342733,7 @@ ${res.warning}`);
|
|
|
342328
342733
|
}
|
|
342329
342734
|
case "todo": {
|
|
342330
342735
|
const sub = positional[0];
|
|
342736
|
+
const nameIn = (names) => (id) => names?.[id] ?? id;
|
|
342331
342737
|
const due = (t) => formatTodoSpan(t);
|
|
342332
342738
|
const call = async (method, url, body2) => {
|
|
342333
342739
|
try {
|
|
@@ -342350,6 +342756,8 @@ ${res.warning}`);
|
|
|
342350
342756
|
const idOf = () => needPos(positional, 1, `oasis todo ${sub} <id>`);
|
|
342351
342757
|
const todoPath = (id, tail = "") => `/api/todos/${encodeURIComponent(id)}${tail}`;
|
|
342352
342758
|
if (sub === "add") {
|
|
342759
|
+
const to = flags.get("to");
|
|
342760
|
+
if (to !== void 0 && !to.trim()) throw new Error("--to \u8981\u5199\u6D3E\u7ED9\u8C01\uFF08actorId \u6216\u540D\u5B57\uFF09");
|
|
342353
342761
|
const title = need(flags, "title");
|
|
342354
342762
|
const dueDate = checkDay(need(flags, "due"), "due");
|
|
342355
342763
|
const time4 = flags.get("time");
|
|
@@ -342360,6 +342768,7 @@ ${res.warning}`);
|
|
|
342360
342768
|
if (start !== void 0 && start > dueDate && isValidDay(start)) throw new Error(`--start ${start} \u665A\u4E8E --due ${dueDate}\uFF1A\u5F00\u59CB\u4E0D\u80FD\u665A\u4E8E\u622A\u6B62`);
|
|
342361
342769
|
const sourceChatSessionId = process.env["OASIS_CHAT_SESSION_ID"];
|
|
342362
342770
|
const r5 = await call("POST", "/api/todos", {
|
|
342771
|
+
...to !== void 0 ? { assignee: to } : {},
|
|
342363
342772
|
title,
|
|
342364
342773
|
dueDate,
|
|
342365
342774
|
...time4 !== void 0 ? { dueTime: checkTime(time4) } : {},
|
|
@@ -342369,22 +342778,39 @@ ${res.warning}`);
|
|
|
342369
342778
|
...sourceChatSessionId ? { sourceChatSessionId } : {}
|
|
342370
342779
|
});
|
|
342371
342780
|
println(r5.todayLine);
|
|
342781
|
+
if (r5.todo.status === "draft") {
|
|
342782
|
+
println(`\u8349\u6848\uFF08\u8FD8\u6CA1\u53D1\u51FA\uFF09\uFF1A\u6D3E\u7ED9 ${nameIn(r5.names)(r5.todo.assigneeActorId)}\uFF1A${r5.todo.title}\uFF0C${due(r5.todo)}`);
|
|
342783
|
+
println(`id\uFF1A${r5.todo.id}`);
|
|
342784
|
+
println("\u5BF9\u8BDD\u91CC\u5DF2\u51FA\u8349\u6848\u5361\uFF1A\u8BF7\u7528\u6237\u6838\u5BF9\u540E\u70B9\u300C\u53D1\u51FA\u300D\u3002\u53D1\u51FA\u524D\u5BF9\u65B9\u770B\u4E0D\u5230\u3001\u4E5F\u6536\u4E0D\u5230\u901A\u77E5\uFF1B\u8981\u6539\u5C31 oasis todo edit \u8FD9\u6761\u8349\u6848\u3002");
|
|
342785
|
+
break;
|
|
342786
|
+
}
|
|
342372
342787
|
println(`\u8BB0\u4E0B\u4E86\uFF1A${r5.todo.title}\uFF0C${due(r5.todo)}`);
|
|
342373
342788
|
println(`id\uFF1A${r5.todo.id}`);
|
|
342374
342789
|
break;
|
|
342375
342790
|
}
|
|
342376
342791
|
if (sub === "list") {
|
|
342792
|
+
const qs = new URLSearchParams();
|
|
342377
342793
|
const all2 = flags.get("all") === "true";
|
|
342378
|
-
|
|
342794
|
+
if (all2) qs.set("all", "1");
|
|
342795
|
+
if (flags.get("everyone") === "true") qs.set("everyone", "1");
|
|
342796
|
+
for (const [flag, key2] of [["assignee", "assignee"], ["creator", "creator"], ["q", "q"], ["status", "status"]]) {
|
|
342797
|
+
const v2 = flags.get(flag);
|
|
342798
|
+
if (v2 !== void 0) qs.set(key2, v2);
|
|
342799
|
+
}
|
|
342800
|
+
if (flags.has("due-from")) qs.set("dueFrom", checkDay(flags.get("due-from"), "due-from"));
|
|
342801
|
+
if (flags.has("due-to")) qs.set("dueTo", checkDay(flags.get("due-to"), "due-to"));
|
|
342802
|
+
const query = qs.toString();
|
|
342803
|
+
const r5 = await call("GET", `/api/todos${query ? `?${query}` : ""}`);
|
|
342379
342804
|
println(r5.todayLine);
|
|
342380
342805
|
if (r5.items.length === 0) {
|
|
342381
|
-
println(all2 ? "\u6CA1\u6709\u5F85\u529E\u3002" : "\u6CA1\u6709\u672A\u5B8C\u6210\u7684\u5F85\u529E\u3002");
|
|
342806
|
+
println(query && query !== "all=1" ? "\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u5F85\u529E\u3002" : all2 ? "\u6CA1\u6709\u5F85\u529E\u3002" : "\u6CA1\u6709\u672A\u5B8C\u6210\u7684\u5F85\u529E\u3002");
|
|
342382
342807
|
break;
|
|
342383
342808
|
}
|
|
342809
|
+
const nm = nameIn(r5.names);
|
|
342384
342810
|
for (const t of r5.items) {
|
|
342385
|
-
const tag = t.status === "done" ? " [\u5DF2\u5B8C\u6210]" : t.status === "cancelled" ? " [\u5DF2\u53D6\u6D88]" : t.dueDate < r5.today.date ? " [\u5DF2\u8FC7\u671F]" : "";
|
|
342811
|
+
const tag = t.status === "draft" ? " [\u8349\u6848\xB7\u672A\u53D1\u51FA]" : t.status === "done" ? " [\u5DF2\u5B8C\u6210]" : t.status === "cancelled" ? " [\u5DF2\u53D6\u6D88]" : t.dueDate < r5.today.date ? " [\u5DF2\u8FC7\u671F]" : "";
|
|
342386
342812
|
const when = t.startDate ? formatTodoSpan(t) : formatTodoDue(t.dueDate, t.dueTime);
|
|
342387
|
-
println(`${t.id} ${when} ${t.title}${tag}`);
|
|
342813
|
+
println(`${t.id} ${when} ${t.title} \u627F\u63A5\uFF1A${nm(t.assigneeActorId)} \u53D1\u8D77\uFF1A${nm(t.creatorActorId)}${tag}`);
|
|
342388
342814
|
}
|
|
342389
342815
|
break;
|
|
342390
342816
|
}
|
|
@@ -342395,11 +342821,27 @@ ${res.warning}`);
|
|
|
342395
342821
|
call("GET", "/api/todos")
|
|
342396
342822
|
]);
|
|
342397
342823
|
const t = one2.todo;
|
|
342398
|
-
const
|
|
342824
|
+
const nm = nameIn(one2.names);
|
|
342825
|
+
const statusText = t.status === "draft" ? "\u8349\u6848\uFF08\u8FD8\u6CA1\u53D1\u51FA\uFF09" : t.status === "done" ? "\u5DF2\u5B8C\u6210" : t.status === "cancelled" ? "\u5DF2\u53D6\u6D88" : t.dueDate < day.today.date ? "\u672A\u5B8C\u6210\uFF08\u5DF2\u8FC7\u671F\uFF09" : "\u672A\u5B8C\u6210";
|
|
342399
342826
|
println(day.todayLine);
|
|
342400
342827
|
println(`${t.id} ${t.title}`);
|
|
342401
342828
|
println(`\u65F6\u95F4\uFF1A${due(t)}`);
|
|
342402
342829
|
println(`\u72B6\u6001\uFF1A${statusText}`);
|
|
342830
|
+
println(`\u53D1\u8D77\u4EBA\uFF1A${nm(t.creatorActorId)}`);
|
|
342831
|
+
println(`\u627F\u63A5\u4EBA\uFF1A${nm(t.assigneeActorId)}`);
|
|
342832
|
+
const fmtAt = (iso9) => new Intl.DateTimeFormat("sv-SE", {
|
|
342833
|
+
timeZone: day.today.timeZone,
|
|
342834
|
+
year: "numeric",
|
|
342835
|
+
month: "2-digit",
|
|
342836
|
+
day: "2-digit",
|
|
342837
|
+
hour: "2-digit",
|
|
342838
|
+
minute: "2-digit",
|
|
342839
|
+
hour12: false
|
|
342840
|
+
}).format(new Date(iso9));
|
|
342841
|
+
if (t.creatorActorId !== t.assigneeActorId) {
|
|
342842
|
+
println(`\u53D1\u51FA\u65F6\u95F4\uFF1A${t.sentAt ? fmtAt(t.sentAt) : "\uFF08\u8FD8\u6CA1\u53D1\u51FA\uFF09"}`);
|
|
342843
|
+
println(`\u627F\u63A5\u4EBA\u9996\u6B21\u67E5\u770B\uFF1A${t.firstViewedAt ? fmtAt(t.firstViewedAt) : "\uFF08\u8FD8\u6CA1\u770B\u8FC7\uFF09"}`);
|
|
342844
|
+
}
|
|
342403
342845
|
if (t.linkedWorkorderId) println(`\u5DE5\u5355\uFF1A${t.linkedWorkorderId}`);
|
|
342404
342846
|
println("\u5907\u6CE8\uFF1A");
|
|
342405
342847
|
println(t.note ? t.note : "\uFF08\u65E0\uFF09");
|
|
@@ -342412,24 +342854,16 @@ ${res.warning}`);
|
|
|
342412
342854
|
startTime: "\u5F00\u59CB\u65F6\u523B",
|
|
342413
342855
|
dueDate: "\u622A\u6B62\u65E5",
|
|
342414
342856
|
dueTime: "\u622A\u6B62\u65F6\u523B",
|
|
342415
|
-
linkedWorkorderId: "\u5DE5\u5355"
|
|
342857
|
+
linkedWorkorderId: "\u5DE5\u5355",
|
|
342858
|
+
assignee: "\u627F\u63A5\u4EBA"
|
|
342416
342859
|
};
|
|
342417
|
-
const STATUS = { open: "\u672A\u5B8C\u6210", done: "\u5DF2\u5B8C\u6210", cancelled: "\u5DF2\u53D6\u6D88" };
|
|
342418
|
-
const val = (field, v2) => v2 === void 0 || v2 === "" ? "\uFF08\u7A7A\uFF09" : field === "status" ? STATUS[v2] ?? v2 : v2;
|
|
342419
|
-
const fmtAt = (iso9) => new Intl.DateTimeFormat("sv-SE", {
|
|
342420
|
-
timeZone: day.today.timeZone,
|
|
342421
|
-
year: "numeric",
|
|
342422
|
-
month: "2-digit",
|
|
342423
|
-
day: "2-digit",
|
|
342424
|
-
hour: "2-digit",
|
|
342425
|
-
minute: "2-digit",
|
|
342426
|
-
hour12: false
|
|
342427
|
-
}).format(new Date(iso9));
|
|
342860
|
+
const STATUS = { draft: "\u8349\u6848", open: "\u672A\u5B8C\u6210", done: "\u5DF2\u5B8C\u6210", cancelled: "\u5DF2\u53D6\u6D88" };
|
|
342861
|
+
const val = (field, v2) => v2 === void 0 || v2 === "" ? "\uFF08\u7A7A\uFF09" : field === "status" ? STATUS[v2] ?? v2 : field === "assignee" ? nm(v2) : v2;
|
|
342428
342862
|
const changes = [...one2.changes].sort((a5, b6) => b6.at.localeCompare(a5.at));
|
|
342429
342863
|
println(`\u53D8\u66F4\u8BB0\u5F55\uFF08${changes.length} \u6761\uFF0C\u65B0\u7684\u5728\u524D\uFF09\uFF1A`);
|
|
342430
342864
|
for (const c5 of changes) {
|
|
342431
|
-
const who = c5.handActorId ? `${c5.actorId}\uFF08\u7ECF\u624B ${c5.handActorId}\uFF09` : c5.actorId;
|
|
342432
|
-
const what = c5.field === "created" ? `\u8BB0\u4E0B\uFF1A${c5.newValue ?? ""}` : `${FIELD[c5.field] ?? c5.field}\uFF1A${val(c5.field, c5.oldValue)} \u2192 ${val(c5.field, c5.newValue)}`;
|
|
342865
|
+
const who = c5.handActorId ? `${nm(c5.actorId)}\uFF08\u7ECF\u624B ${c5.handActorId}\uFF09` : nm(c5.actorId);
|
|
342866
|
+
const what = c5.field === "created" ? `\u8BB0\u4E0B\uFF1A${c5.newValue ?? ""}` : c5.field === "sent" ? `\u53D1\u51FA\uFF1A\u6D3E\u7ED9 ${nm(c5.newValue ?? "")}` : `${FIELD[c5.field] ?? c5.field}\uFF1A${val(c5.field, c5.oldValue)} \u2192 ${val(c5.field, c5.newValue)}`;
|
|
342433
342867
|
println(`- ${fmtAt(c5.at)} ${who} ${what}${c5.reason ? ` \u539F\u56E0\uFF1A${c5.reason}` : ""}`);
|
|
342434
342868
|
}
|
|
342435
342869
|
break;
|
|
@@ -342444,7 +342878,8 @@ ${res.warning}`);
|
|
|
342444
342878
|
if (flags.has("start")) body2["startDate"] = flags.get("start") === "none" ? null : checkDay(flags.get("start"), "start");
|
|
342445
342879
|
if (flags.has("start-time")) body2["startTime"] = flags.get("start-time") === "none" ? null : checkTime(flags.get("start-time"), "start-time");
|
|
342446
342880
|
if (flags.has("workorder")) body2["linkedWorkorderId"] = flags.get("workorder") === "none" ? null : flags.get("workorder");
|
|
342447
|
-
if (
|
|
342881
|
+
if (flags.has("to")) body2["assignee"] = flags.get("to");
|
|
342882
|
+
if (Object.keys(body2).length === 0) throw new Error("\u6CA1\u6709\u8981\u6539\u7684\u5B57\u6BB5\uFF1A\u7ED9 --title / --note / --start / --start-time / --due / --time / --workorder / --to \u91CC\u7684\u81F3\u5C11\u4E00\u4E2A");
|
|
342448
342883
|
if (flags.get("reason")) body2["reason"] = flags.get("reason");
|
|
342449
342884
|
const r5 = await call("PATCH", todoPath(id), body2);
|
|
342450
342885
|
println(r5.todayLine);
|
|
@@ -342453,7 +342888,7 @@ ${res.warning}`);
|
|
|
342453
342888
|
}
|
|
342454
342889
|
if (sub === "done" || sub === "cancel" || sub === "reopen") {
|
|
342455
342890
|
const id = idOf();
|
|
342456
|
-
const reason =
|
|
342891
|
+
const reason = flags.get("reason");
|
|
342457
342892
|
const r5 = await call("POST", todoPath(id, `/${sub}`), reason ? { reason } : {});
|
|
342458
342893
|
println(r5.todayLine);
|
|
342459
342894
|
println(`${sub === "done" ? "\u5DF2\u5B8C\u6210" : sub === "cancel" ? "\u5DF2\u53D6\u6D88" : "\u5DF2\u91CD\u65B0\u6253\u5F00"}\uFF1A${r5.todo.title}\uFF0C${due(r5.todo)}`);
|
|
@@ -343284,10 +343719,10 @@ ${res.warning}`);
|
|
|
343284
343719
|
println(`\u4F9D\u8D56: ${v2.artifact.inputs.length === 0 ? "\u65E0" : v2.artifact.inputs.map((e5) => `${e5.to}@${e5.pinned ?? "\u672A\u5C31\u7EEA"}${e5.required ? "[required]" : ""}`).join(" ")}`);
|
|
343285
343720
|
println(`\u5185\u5BB9: ${kind} \u2190 \u53D6\u6B63\u6587\u7528 \`oasis content ${id} [path]\`\uFF08current head\uFF09\uFF0F\`oasis content <revisionId>\`\uFF08\u6307\u5B9A\u7248\u672C\uFF09`);
|
|
343286
343721
|
const REV_LIMIT = 50;
|
|
343287
|
-
const
|
|
343288
|
-
println(`\u7248\u672C(${v2.revisions.length})${
|
|
343289
|
-
if (
|
|
343290
|
-
for (const r5 of
|
|
343722
|
+
const shown2 = v2.revisions.length > REV_LIMIT ? v2.revisions.slice(-REV_LIMIT) : v2.revisions;
|
|
343723
|
+
println(`\u7248\u672C(${v2.revisions.length})${shown2.length < v2.revisions.length ? `\u2014\u2014\u53EA\u5217\u6700\u8FD1 ${REV_LIMIT} \u6761` : ""}\uFF1A`);
|
|
343724
|
+
if (shown2.length === 0) println(` \uFF08\u65E0\uFF09`);
|
|
343725
|
+
for (const r5 of shown2) {
|
|
343291
343726
|
const marks = [
|
|
343292
343727
|
r5.id === v2.artifact.currentRev ? "\u2190 current head" : "",
|
|
343293
343728
|
r5.id === v2.concludedHead ? "\u2190 \u5DF2\u4EA4\u4ED8" : ""
|
|
@@ -343360,11 +343795,11 @@ ${res.warning}`);
|
|
|
343360
343795
|
case "ls": {
|
|
343361
343796
|
const wsFilter = flags.get("workspace");
|
|
343362
343797
|
const rows = await api.view("ls");
|
|
343363
|
-
const
|
|
343364
|
-
for (const a5 of
|
|
343798
|
+
const shown2 = wsFilter ? rows.filter((a5) => a5.workspace === wsFilter) : rows;
|
|
343799
|
+
for (const a5 of shown2) {
|
|
343365
343800
|
println(`${a5.id} ws=${a5.workspace ?? "\u2205"} type=${a5.type} owner=${a5.owner} head=${a5.currentRev ?? "\u2205"} queue=${a5.queue} [${a5.mark}]`);
|
|
343366
343801
|
}
|
|
343367
|
-
if (wsFilter &&
|
|
343802
|
+
if (wsFilter && shown2.length === 0) println(`\uFF08${wsFilter} \u4E0B\u6CA1\u6709\u4EA7\u7269\u2014\u2014\u5DE5\u5355 id \u7528 \`oasis workorders\` \u67E5\uFF09`);
|
|
343368
343803
|
else if (!wsFilter && rows.length > 30) println(`\u2014\u2014 \u5171 ${rows.length} \u4E2A\u4EA7\u7269\uFF08\u5168\u5E93\u62C9\u5E73\uFF09\u3002\u6309\u5DE5\u5355\u770B\u7528 \`oasis ls --workspace <ws:id>\`\uFF0C\u5DE5\u5355\u6E05\u5355\u7528 \`oasis workorders\`\u3002`);
|
|
343369
343804
|
break;
|
|
343370
343805
|
}
|
|
@@ -343930,7 +344365,14 @@ function syncRuntimeAssets(candidateRoots, binDir) {
|
|
|
343930
344365
|
}
|
|
343931
344366
|
function runtimeAssetSourceRoots(input) {
|
|
343932
344367
|
const roots = [];
|
|
343933
|
-
if (input.executingBundle)
|
|
344368
|
+
if (input.executingBundle) {
|
|
344369
|
+
let bundle = input.executingBundle;
|
|
344370
|
+
try {
|
|
344371
|
+
bundle = fs53.realpathSync(bundle);
|
|
344372
|
+
} catch {
|
|
344373
|
+
}
|
|
344374
|
+
roots.push(path47.dirname(bundle));
|
|
344375
|
+
}
|
|
343934
344376
|
try {
|
|
343935
344377
|
roots.push(fs53.realpathSync(input.currentLink));
|
|
343936
344378
|
} catch {
|
|
@@ -344134,7 +344576,7 @@ function shimScript() {
|
|
|
344134
344576
|
}
|
|
344135
344577
|
|
|
344136
344578
|
// src/index.ts
|
|
344137
|
-
var PKG_VERSION = true ? "2.2.
|
|
344579
|
+
var PKG_VERSION = true ? "2.2.122" : "dev";
|
|
344138
344580
|
var LOCAL_BIN = localBin();
|
|
344139
344581
|
var NPM_PREFIX = npmPrefix();
|
|
344140
344582
|
var INSTANCE = DEFAULT_INSTANCE;
|