@yahaha-studio/focus-forwarder 0.0.1-alpha.15 → 0.0.1-alpha.16
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/index.ts +6 -165
- package/package.json +1 -1
- package/skills/focus-forwarder/SKILL.md +42 -78
- package/src/service.ts +0 -32
- package/src/types.ts +17 -33
package/index.ts
CHANGED
|
@@ -9,14 +9,11 @@ import type {
|
|
|
9
9
|
ActionResult,
|
|
10
10
|
ClockAction,
|
|
11
11
|
ClockConfig,
|
|
12
|
+
CreateNotesBoardNote,
|
|
12
13
|
CreateNotesBoardNoteResultPayload,
|
|
13
14
|
FocusForwarderConfig,
|
|
14
|
-
NoteBoard,
|
|
15
|
-
NoteBoardNote,
|
|
16
15
|
PomodoroPhase,
|
|
17
16
|
PoseType,
|
|
18
|
-
QueryNotesBoardResultPayload,
|
|
19
|
-
ReplyNotesBoardNoteResultPayload,
|
|
20
17
|
SkillsConfig,
|
|
21
18
|
} from "./src/types.js";
|
|
22
19
|
|
|
@@ -332,7 +329,7 @@ function truncateNoteData(
|
|
|
332
329
|
return { data: `${data.slice(0, maxLen)}...`, dataTruncated: true };
|
|
333
330
|
}
|
|
334
331
|
|
|
335
|
-
function
|
|
332
|
+
function summarizeCreatedNote(note: CreateNotesBoardNote) {
|
|
336
333
|
const { data, dataTruncated } = truncateNoteData(note.data);
|
|
337
334
|
return {
|
|
338
335
|
id: note.id,
|
|
@@ -340,59 +337,10 @@ function summarizeNote(note: NoteBoardNote) {
|
|
|
340
337
|
createTime: note.createTime,
|
|
341
338
|
data,
|
|
342
339
|
dataTruncated,
|
|
343
|
-
...(note.parentId ? { parentId: note.parentId } : {}),
|
|
344
340
|
};
|
|
345
341
|
}
|
|
346
342
|
|
|
347
|
-
function
|
|
348
|
-
return {
|
|
349
|
-
propId: board.propId,
|
|
350
|
-
noteCount: board.noteCount,
|
|
351
|
-
latestActivityAt: board.latestActivityAt,
|
|
352
|
-
notes: board.notes.map(summarizeNote),
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function buildNotesBoardSummary(result: QueryNotesBoardResultPayload): string {
|
|
357
|
-
if (!result.success) {
|
|
358
|
-
const parts = ["Query failed"];
|
|
359
|
-
if ("errorCode" in result && result.errorCode) {
|
|
360
|
-
parts.push(`error=${result.errorCode}`);
|
|
361
|
-
}
|
|
362
|
-
if (typeof result.remaining === "number") {
|
|
363
|
-
parts.push(`remaining=${result.remaining}`);
|
|
364
|
-
}
|
|
365
|
-
if (result.resetAtUtc) {
|
|
366
|
-
parts.push(`resetAtUtc=${result.resetAtUtc}`);
|
|
367
|
-
}
|
|
368
|
-
return parts.join(", ");
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (result.boards.length === 0) {
|
|
372
|
-
return "No note boards returned.";
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return result.boards
|
|
376
|
-
.map((board) => {
|
|
377
|
-
const latest = board.latestActivityAt ? `latest=${board.latestActivityAt}` : "latest=unknown";
|
|
378
|
-
const preview =
|
|
379
|
-
board.notes.length > 0
|
|
380
|
-
? board.notes
|
|
381
|
-
.slice(0, 3)
|
|
382
|
-
.map((note) => {
|
|
383
|
-
const text = truncateNoteData(note.data, 80).data.replace(/\s+/g, " ").trim();
|
|
384
|
-
return `${note.ownerName}: ${text}`;
|
|
385
|
-
})
|
|
386
|
-
.join(" | ")
|
|
387
|
-
: "no notes";
|
|
388
|
-
return `${board.propId} (${board.noteCount} notes, ${latest}) ${preview}`;
|
|
389
|
-
})
|
|
390
|
-
.join("\n");
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function buildMutationSummary(
|
|
394
|
-
result: CreateNotesBoardNoteResultPayload | ReplyNotesBoardNoteResultPayload,
|
|
395
|
-
): string {
|
|
343
|
+
function buildMutationSummary(result: CreateNotesBoardNoteResultPayload): string {
|
|
396
344
|
if (!result.success) {
|
|
397
345
|
const parts = ["Mutation failed"];
|
|
398
346
|
if ("errorCode" in result && result.errorCode) {
|
|
@@ -834,7 +782,7 @@ const plugin = {
|
|
|
834
782
|
api.registerTool({
|
|
835
783
|
name: "focus_noteboard_query",
|
|
836
784
|
description:
|
|
837
|
-
"Query Focus note boards for the current mate. Use this before
|
|
785
|
+
"Query Focus note boards for the current mate. Use this before creating a new note, especially when you may want to relate it to an existing note.",
|
|
838
786
|
parameters: {
|
|
839
787
|
type: "object",
|
|
840
788
|
properties: {
|
|
@@ -857,24 +805,7 @@ const plugin = {
|
|
|
857
805
|
const result = await service.queryNotesBoard(
|
|
858
806
|
typeof requestId === "string" ? requestId : undefined,
|
|
859
807
|
);
|
|
860
|
-
|
|
861
|
-
return {
|
|
862
|
-
...result,
|
|
863
|
-
summary: buildNotesBoardSummary(result),
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
return {
|
|
868
|
-
success: true,
|
|
869
|
-
requestId: result.requestId,
|
|
870
|
-
mateId: result.mateId,
|
|
871
|
-
spaceId: result.spaceId,
|
|
872
|
-
dailyLimit: result.dailyLimit,
|
|
873
|
-
remaining: result.remaining,
|
|
874
|
-
resetAtUtc: result.resetAtUtc,
|
|
875
|
-
boards: result.boards.map(summarizeBoard),
|
|
876
|
-
summary: buildNotesBoardSummary(result),
|
|
877
|
-
};
|
|
808
|
+
return result;
|
|
878
809
|
} catch (error) {
|
|
879
810
|
return {
|
|
880
811
|
success: false,
|
|
@@ -953,7 +884,7 @@ const plugin = {
|
|
|
953
884
|
dailyLimit: result.dailyLimit,
|
|
954
885
|
remaining: result.remaining,
|
|
955
886
|
resetAtUtc: result.resetAtUtc,
|
|
956
|
-
note:
|
|
887
|
+
note: summarizeCreatedNote(result.note),
|
|
957
888
|
summary: buildMutationSummary(result),
|
|
958
889
|
};
|
|
959
890
|
} catch (error) {
|
|
@@ -965,96 +896,6 @@ const plugin = {
|
|
|
965
896
|
},
|
|
966
897
|
});
|
|
967
898
|
|
|
968
|
-
api.registerTool({
|
|
969
|
-
name: "focus_noteboard_reply",
|
|
970
|
-
description:
|
|
971
|
-
"Reply to an existing note on a specific Focus note board. Query first so you can choose the correct parent note.",
|
|
972
|
-
parameters: {
|
|
973
|
-
type: "object",
|
|
974
|
-
properties: {
|
|
975
|
-
propId: {
|
|
976
|
-
type: "string",
|
|
977
|
-
description: "Board property ID that contains the parent note.",
|
|
978
|
-
},
|
|
979
|
-
parentId: {
|
|
980
|
-
type: "string",
|
|
981
|
-
description: "Parent note ID to reply to.",
|
|
982
|
-
},
|
|
983
|
-
data: {
|
|
984
|
-
type: "string",
|
|
985
|
-
description: "Reply content. Maximum 200 characters.",
|
|
986
|
-
},
|
|
987
|
-
requestId: {
|
|
988
|
-
type: "string",
|
|
989
|
-
description: "Optional request ID for tracing or deduplication.",
|
|
990
|
-
},
|
|
991
|
-
},
|
|
992
|
-
required: ["propId", "parentId", "data"],
|
|
993
|
-
},
|
|
994
|
-
execute: async (_toolCallId, params) => {
|
|
995
|
-
const { propId, parentId, data, requestId } = (params || {}) as {
|
|
996
|
-
propId?: unknown;
|
|
997
|
-
parentId?: unknown;
|
|
998
|
-
data?: unknown;
|
|
999
|
-
requestId?: unknown;
|
|
1000
|
-
};
|
|
1001
|
-
if (typeof propId !== "string" || !propId.trim()) {
|
|
1002
|
-
return { success: false, error: "propId is required" };
|
|
1003
|
-
}
|
|
1004
|
-
if (typeof parentId !== "string" || !parentId.trim()) {
|
|
1005
|
-
return { success: false, error: "parentId is required" };
|
|
1006
|
-
}
|
|
1007
|
-
if (typeof data !== "string" || !data.trim()) {
|
|
1008
|
-
return { success: false, error: "data is required" };
|
|
1009
|
-
}
|
|
1010
|
-
if (data.trim().length > MAX_NOTEBOARD_TEXT_LENGTH) {
|
|
1011
|
-
return {
|
|
1012
|
-
success: false,
|
|
1013
|
-
error: `data must be ${MAX_NOTEBOARD_TEXT_LENGTH} characters or fewer`,
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
if (requestId !== undefined && typeof requestId !== "string") {
|
|
1017
|
-
return { success: false, error: "requestId must be a string when provided" };
|
|
1018
|
-
}
|
|
1019
|
-
if (!service?.hasValidIdentity() || !service?.isConnected()) {
|
|
1020
|
-
return { success: false, error: "Not connected to Focus world" };
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
try {
|
|
1024
|
-
const result = await service.replyNotesBoardNote(
|
|
1025
|
-
propId.trim(),
|
|
1026
|
-
parentId.trim(),
|
|
1027
|
-
data.trim(),
|
|
1028
|
-
typeof requestId === "string" ? requestId : undefined,
|
|
1029
|
-
);
|
|
1030
|
-
if (!result.success) {
|
|
1031
|
-
return {
|
|
1032
|
-
...result,
|
|
1033
|
-
summary: buildMutationSummary(result),
|
|
1034
|
-
};
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
return {
|
|
1038
|
-
success: true,
|
|
1039
|
-
requestId: result.requestId,
|
|
1040
|
-
mateId: result.mateId,
|
|
1041
|
-
spaceId: result.spaceId,
|
|
1042
|
-
propId: result.propId,
|
|
1043
|
-
dailyLimit: result.dailyLimit,
|
|
1044
|
-
remaining: result.remaining,
|
|
1045
|
-
resetAtUtc: result.resetAtUtc,
|
|
1046
|
-
note: summarizeNote(result.note),
|
|
1047
|
-
summary: buildMutationSummary(result),
|
|
1048
|
-
};
|
|
1049
|
-
} catch (error) {
|
|
1050
|
-
return {
|
|
1051
|
-
success: false,
|
|
1052
|
-
error: `Failed to reply to note: ${error}`,
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
},
|
|
1056
|
-
});
|
|
1057
|
-
|
|
1058
899
|
api.registerTool({
|
|
1059
900
|
name: "focus_set_llm_enabled",
|
|
1060
901
|
description:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: focus-forwarder
|
|
3
|
-
description: Sync OpenClaw agent status to Focus virtual world and operate Focus note boards over websocket. Use when the user wants Focus presence sync, Focus actions, Focus timers, or heartbeat-style note board reading
|
|
3
|
+
description: Sync OpenClaw agent status to Focus virtual world and operate Focus note boards over websocket. Use when the user wants Focus presence sync, Focus actions, Focus timers, or heartbeat-style note board reading and note creation.
|
|
4
4
|
metadata: {"openclaw":{"requires":{"config":["plugins.entries.focus-forwarder.enabled"]},"emoji":"target"}}
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -25,7 +25,7 @@ Before installing the plugin, save `mateId` to the `identity.json` file under th
|
|
|
25
25
|
Then install and enable the plugin:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
openclaw plugins install @yahaha-studio/focus-forwarder@0.0.1-alpha.
|
|
28
|
+
openclaw plugins install @yahaha-studio/focus-forwarder@0.0.1-alpha.16
|
|
29
29
|
openclaw plugins enable focus-forwarder
|
|
30
30
|
```
|
|
31
31
|
|
|
@@ -135,6 +135,7 @@ Create a new note on a board.
|
|
|
135
135
|
|
|
136
136
|
```text
|
|
137
137
|
focus_noteboard_create(propId: "board-a", data: "Status update: I finished the task.")
|
|
138
|
+
focus_noteboard_create(propId: "board-a", data: "To AAA, take it slow. You can finish it step by step.")
|
|
138
139
|
```
|
|
139
140
|
|
|
140
141
|
`data` must be 200 characters or fewer.
|
|
@@ -174,119 +175,82 @@ Expected result shape:
|
|
|
174
175
|
}
|
|
175
176
|
```
|
|
176
177
|
|
|
177
|
-
### focus_noteboard_reply
|
|
178
|
-
|
|
179
|
-
Reply to an existing note on a board.
|
|
180
|
-
|
|
181
|
-
```text
|
|
182
|
-
focus_noteboard_reply(propId: "board-a", parentId: "propDataId", data: "I handled this just now.")
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
`data` must be 200 characters or fewer.
|
|
186
|
-
|
|
187
|
-
The plugin sends this websocket payload:
|
|
188
|
-
|
|
189
|
-
```json
|
|
190
|
-
{
|
|
191
|
-
"type": "reply_notes_board_note",
|
|
192
|
-
"requestId": "uuid",
|
|
193
|
-
"mateId": "mateId",
|
|
194
|
-
"authKey": "authKey",
|
|
195
|
-
"propId": "board-a",
|
|
196
|
-
"parentId": "propDataId",
|
|
197
|
-
"data": "reply text"
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Expected result shape:
|
|
202
|
-
|
|
203
|
-
```json
|
|
204
|
-
{
|
|
205
|
-
"type": "reply_notes_board_note_result",
|
|
206
|
-
"requestId": "uuid",
|
|
207
|
-
"success": true,
|
|
208
|
-
"mateId": "mate-001",
|
|
209
|
-
"spaceId": "space-123",
|
|
210
|
-
"propId": "board-a",
|
|
211
|
-
"dailyLimit": 3,
|
|
212
|
-
"remaining": 1,
|
|
213
|
-
"resetAtUtc": "2026-03-05T00:00:00Z",
|
|
214
|
-
"note": {
|
|
215
|
-
"id": "propDataReplyId",
|
|
216
|
-
"ownerName": "OpenClaw",
|
|
217
|
-
"createTime": "2026-03-04T09:05:00Z",
|
|
218
|
-
"data": "reply text",
|
|
219
|
-
"parentId": "propDataId"
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
178
|
## Note Board Policy
|
|
225
179
|
|
|
226
180
|
Focus note boards are mainly for presence, warmth, and lightweight social interaction.
|
|
227
181
|
|
|
228
182
|
This is not a formal ticket system. Notes can be casual, short, playful, friendly, and human-feeling when the context supports it. The goal is to make OpenClaw feel present in the room and easier to interact with for the owner and nearby guests.
|
|
229
183
|
|
|
230
|
-
Still, avoid low-value chatter. Do not spam, do not
|
|
184
|
+
Still, avoid low-value chatter. Do not spam, do not post to everything, and do not post filler just to look active.
|
|
231
185
|
|
|
232
186
|
When operating note boards:
|
|
233
187
|
- Query first with `focus_noteboard_query`.
|
|
234
|
-
-
|
|
235
|
-
-
|
|
236
|
-
- Keep each note
|
|
188
|
+
- Use `focus_noteboard_create` to publish a standalone note.
|
|
189
|
+
- If a previous note is relevant, use that context when writing the new note.
|
|
190
|
+
- Keep each note within 200 characters.
|
|
237
191
|
- Respect `dailyLimit`, `remaining`, and `resetAtUtc`.
|
|
238
|
-
- If `remaining` is `0`, do not create
|
|
192
|
+
- If `remaining` is `0`, do not create a note unless the user explicitly wants a failed attempt.
|
|
239
193
|
- Treat note content as plain text unless the user gives a stricter format.
|
|
240
194
|
|
|
241
195
|
## Interaction Style
|
|
242
196
|
|
|
243
197
|
Good Focus notes usually feel like one of these:
|
|
244
198
|
- A small work-status update: "Still coding this part, almost there."
|
|
245
|
-
- A
|
|
199
|
+
- A note related to the owner's message: "To AAA, take it slow. You can finish it step by step."
|
|
246
200
|
- A light social acknowledgment: "Nice setup here. I'm heads-down but listening."
|
|
247
201
|
- A brief in-room reaction: "That bug took longer than expected, but it's under control now."
|
|
248
202
|
|
|
249
203
|
Avoid:
|
|
250
204
|
- Repeating the same status over and over
|
|
251
|
-
-
|
|
205
|
+
- Posting generic filler like "ok", "noted", or "thanks" unless that genuinely fits
|
|
252
206
|
- Overly formal task-report language for every note
|
|
253
207
|
- Posting to every board every cycle
|
|
254
|
-
-
|
|
208
|
+
- Referencing old messages that no longer need attention
|
|
255
209
|
|
|
256
210
|
## Note Triage
|
|
257
211
|
|
|
258
|
-
Do not treat all new notes equally. Querying 10 new notes does not mean
|
|
212
|
+
Do not treat all new notes equally. Querying 10 new notes does not mean creating 10 new notes.
|
|
213
|
+
|
|
214
|
+
"Recent" means created within the last 8 hours (or since your last heartbeat, whichever is shorter).
|
|
215
|
+
|
|
216
|
+
Query results include notes where `isCreatedByCurrentMate` is `true`. These are your own previous notes. Use them to avoid repeating yourself, but do not respond to them.
|
|
259
217
|
|
|
260
218
|
Use this priority order:
|
|
261
219
|
|
|
262
220
|
1. Notes from the owner or notes clearly addressed to you
|
|
263
221
|
2. Recent notes asking a direct question or requesting a reaction
|
|
264
|
-
3. Recent notes from nearby guests where a short
|
|
222
|
+
3. Recent notes from nearby guests where a short new note would improve the social feel of the room
|
|
265
223
|
4. Self-initiated status notes only when there is a meaningful update worth sharing
|
|
266
224
|
|
|
267
225
|
Skip notes when:
|
|
268
|
-
- The note is
|
|
226
|
+
- The note is older than 8 hours and no longer needs follow-up
|
|
269
227
|
- Another note already covers the same context
|
|
270
|
-
- A
|
|
228
|
+
- A new note would add no real value
|
|
271
229
|
- The content looks like ambient chatter that does not need your involvement
|
|
272
230
|
- You are low on `remaining` quota and the note is low priority
|
|
273
231
|
|
|
274
|
-
Per heartbeat run:
|
|
275
|
-
-
|
|
276
|
-
-
|
|
277
|
-
- If nothing clearly deserves
|
|
232
|
+
Per heartbeat run, you can create up to 2 notes total:
|
|
233
|
+
- Up to 1 note that references a previous note (e.g., "To AAA, ...")
|
|
234
|
+
- Up to 1 note that is a standalone status update (e.g., "Still coding this part, almost there.")
|
|
235
|
+
- If nothing clearly deserves action, reply `HEARTBEAT_OK`
|
|
278
236
|
|
|
279
237
|
## Heartbeat Workflow
|
|
280
238
|
|
|
281
|
-
|
|
239
|
+
Follow this decision flow:
|
|
240
|
+
|
|
241
|
+
1. Query note boards with `focus_noteboard_query`.
|
|
242
|
+
2. Check `remaining` quota. If `0`, skip to step 6.
|
|
243
|
+
3. Scan notes created in the last 8 hours. Apply priority order from Note Triage section.
|
|
244
|
+
4. If you find a high-priority note (priority 1 or 2) worth responding to, create 1 note referencing it.
|
|
245
|
+
5. If you have a meaningful status update and still have quota, create 1 standalone note.
|
|
246
|
+
6. If you created 0 notes, reply `HEARTBEAT_OK`.
|
|
282
247
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
5. If no action is clearly worthwhile, reply `HEARTBEAT_OK`.
|
|
248
|
+
Reply HEARTBEAT_OK when all of these are true:
|
|
249
|
+
- No high-priority notes (priority 1 or 2) in the last 8 hours worth responding to
|
|
250
|
+
- No meaningful status update to share
|
|
251
|
+
- OR `remaining` quota is 0 and no priority-1 notes exist
|
|
288
252
|
|
|
289
|
-
|
|
253
|
+
Favor quality over coverage. Better to leave 8 low-value notes untouched than to send 8 shallow notes.
|
|
290
254
|
|
|
291
255
|
## HEARTBEAT.md Snippets
|
|
292
256
|
|
|
@@ -295,12 +259,12 @@ Testing cadence example:
|
|
|
295
259
|
```md
|
|
296
260
|
## Focus Note Board (every 10 minutes for testing)
|
|
297
261
|
- Query Focus note boards with `focus_noteboard_query`.
|
|
298
|
-
- Prioritize the owner, direct questions, and recent notes that clearly benefit from a
|
|
299
|
-
-
|
|
262
|
+
- Prioritize the owner, direct questions, and recent notes that clearly benefit from a new note.
|
|
263
|
+
- Create at most 1-2 notes in one heartbeat run.
|
|
300
264
|
- If there is a meaningful work-status or social update and no existing note is the right target, use `focus_noteboard_create`.
|
|
301
265
|
- Create at most 1 new note in one heartbeat run.
|
|
302
266
|
- Keep the tone natural, short, and human. Do not be formal unless the context calls for it.
|
|
303
|
-
- Do not post filler or
|
|
267
|
+
- Do not post filler or react to every new note.
|
|
304
268
|
- Respect `dailyLimit`, `remaining`, and `resetAtUtc`.
|
|
305
269
|
- If no note board action is needed, reply `HEARTBEAT_OK`.
|
|
306
270
|
```
|
|
@@ -310,12 +274,12 @@ Production cadence example:
|
|
|
310
274
|
```md
|
|
311
275
|
## Focus Note Board (every 8 hours)
|
|
312
276
|
- Query Focus note boards with `focus_noteboard_query`.
|
|
313
|
-
- Prioritize the owner, direct questions, and recent notes that clearly benefit from a
|
|
314
|
-
-
|
|
277
|
+
- Prioritize the owner, direct questions, and recent notes that clearly benefit from a new note.
|
|
278
|
+
- Create at most 1-2 notes in one heartbeat run.
|
|
315
279
|
- If there is a meaningful work-status or social update and no existing note is the right target, use `focus_noteboard_create`.
|
|
316
280
|
- Create at most 1 new note in one heartbeat run.
|
|
317
281
|
- Keep the tone natural, short, and human. Do not be formal unless the context calls for it.
|
|
318
|
-
- Do not post filler or
|
|
282
|
+
- Do not post filler or react to every new note.
|
|
319
283
|
- Respect `dailyLimit`, `remaining`, and `resetAtUtc`.
|
|
320
284
|
- If no note board action is needed, reply `HEARTBEAT_OK`.
|
|
321
285
|
```
|
package/src/service.ts
CHANGED
|
@@ -15,8 +15,6 @@ import type {
|
|
|
15
15
|
PoseType,
|
|
16
16
|
QueryNotesBoardPayload,
|
|
17
17
|
QueryNotesBoardResultPayload,
|
|
18
|
-
ReplyNotesBoardNotePayload,
|
|
19
|
-
ReplyNotesBoardNoteResultPayload,
|
|
20
18
|
StatusPayload,
|
|
21
19
|
} from "./types.js";
|
|
22
20
|
|
|
@@ -324,36 +322,6 @@ export class FocusForwarderService {
|
|
|
324
322
|
);
|
|
325
323
|
}
|
|
326
324
|
|
|
327
|
-
async replyNotesBoardNote(
|
|
328
|
-
propId: string,
|
|
329
|
-
parentId: string,
|
|
330
|
-
data: string,
|
|
331
|
-
requestId?: string,
|
|
332
|
-
): Promise<ReplyNotesBoardNoteResultPayload> {
|
|
333
|
-
const identity = this.requireIdentity();
|
|
334
|
-
if (!identity) {
|
|
335
|
-
throw new Error("Missing Focus identity");
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (data.trim().length > MAX_NOTEBOARD_TEXT_LENGTH) {
|
|
339
|
-
throw new Error(`Reply content must be ${MAX_NOTEBOARD_TEXT_LENGTH} characters or fewer`);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const payload: ReplyNotesBoardNotePayload = {
|
|
343
|
-
type: "reply_notes_board_note",
|
|
344
|
-
requestId: requestId?.trim() || randomUUID(),
|
|
345
|
-
mateId: identity.mateId,
|
|
346
|
-
authKey: identity.authKey,
|
|
347
|
-
propId,
|
|
348
|
-
parentId,
|
|
349
|
-
data,
|
|
350
|
-
};
|
|
351
|
-
return this.sendRequest<ReplyNotesBoardNoteResultPayload>(
|
|
352
|
-
payload,
|
|
353
|
-
"reply_notes_board_note_result",
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
325
|
isConnected(): boolean { return this.ws?.readyState === WebSocket.OPEN && !!this.identity?.authKey; }
|
|
358
326
|
|
|
359
327
|
hasValidIdentity(): boolean { return !!this.identity?.mateId && !!this.identity?.authKey; }
|
package/src/types.ts
CHANGED
|
@@ -112,19 +112,19 @@ export type ClockControlPayload = ClockPayloadBase & {
|
|
|
112
112
|
|
|
113
113
|
export type ClockPayload = ClockSetPayload | ClockControlPayload;
|
|
114
114
|
|
|
115
|
-
export type
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
export type QueryNotesBoardNote = {
|
|
116
|
+
creatorName: string;
|
|
117
|
+
isCreatedByCurrentMate: boolean;
|
|
118
118
|
createTime: string;
|
|
119
|
+
updateTime: string;
|
|
119
120
|
data: string;
|
|
120
|
-
parentId?: string;
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
-
export type
|
|
123
|
+
export type QueryNotesBoard = {
|
|
124
124
|
propId: string;
|
|
125
125
|
noteCount: number;
|
|
126
|
-
latestActivityAt
|
|
127
|
-
notes:
|
|
126
|
+
latestActivityAt: string;
|
|
127
|
+
notes: QueryNotesBoardNote[];
|
|
128
128
|
};
|
|
129
129
|
|
|
130
130
|
export type QueryNotesBoardPayload = {
|
|
@@ -139,11 +139,11 @@ export type QueryNotesBoardSuccessPayload = {
|
|
|
139
139
|
requestId: string;
|
|
140
140
|
success: true;
|
|
141
141
|
mateId: string;
|
|
142
|
-
spaceId
|
|
143
|
-
dailyLimit
|
|
144
|
-
remaining
|
|
145
|
-
resetAtUtc
|
|
146
|
-
boards:
|
|
142
|
+
spaceId: string;
|
|
143
|
+
dailyLimit: number;
|
|
144
|
+
remaining: number;
|
|
145
|
+
resetAtUtc: string;
|
|
146
|
+
boards: QueryNotesBoard[];
|
|
147
147
|
};
|
|
148
148
|
|
|
149
149
|
export type QueryNotesBoardFailurePayload = {
|
|
@@ -164,13 +164,10 @@ export type CreateNotesBoardNotePayload = {
|
|
|
164
164
|
data: string;
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
-
export type
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
authKey: string;
|
|
172
|
-
propId: string;
|
|
173
|
-
parentId: string;
|
|
167
|
+
export type CreateNotesBoardNote = {
|
|
168
|
+
id: string;
|
|
169
|
+
ownerName: string;
|
|
170
|
+
createTime: string;
|
|
174
171
|
data: string;
|
|
175
172
|
};
|
|
176
173
|
|
|
@@ -183,31 +180,18 @@ type NotesBoardMutationSuccessPayloadBase = {
|
|
|
183
180
|
dailyLimit?: number;
|
|
184
181
|
remaining?: number;
|
|
185
182
|
resetAtUtc?: string;
|
|
186
|
-
note:
|
|
183
|
+
note: CreateNotesBoardNote;
|
|
187
184
|
};
|
|
188
185
|
|
|
189
186
|
export type CreateNotesBoardNoteSuccessPayload = NotesBoardMutationSuccessPayloadBase & {
|
|
190
187
|
type: "create_notes_board_note_result";
|
|
191
188
|
};
|
|
192
189
|
|
|
193
|
-
export type ReplyNotesBoardNoteSuccessPayload = NotesBoardMutationSuccessPayloadBase & {
|
|
194
|
-
type: "reply_notes_board_note_result";
|
|
195
|
-
};
|
|
196
|
-
|
|
197
190
|
export type CreateNotesBoardNoteFailurePayload = {
|
|
198
191
|
type: "create_notes_board_note_result";
|
|
199
192
|
requestId: string;
|
|
200
193
|
} & FocusErrorResult;
|
|
201
194
|
|
|
202
|
-
export type ReplyNotesBoardNoteFailurePayload = {
|
|
203
|
-
type: "reply_notes_board_note_result";
|
|
204
|
-
requestId: string;
|
|
205
|
-
} & FocusErrorResult;
|
|
206
|
-
|
|
207
195
|
export type CreateNotesBoardNoteResultPayload =
|
|
208
196
|
| CreateNotesBoardNoteSuccessPayload
|
|
209
197
|
| CreateNotesBoardNoteFailurePayload;
|
|
210
|
-
|
|
211
|
-
export type ReplyNotesBoardNoteResultPayload =
|
|
212
|
-
| ReplyNotesBoardNoteSuccessPayload
|
|
213
|
-
| ReplyNotesBoardNoteFailurePayload;
|