teamclaws 2.0.1 → 2.0.3
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/README.md +24 -0
- package/dist/app.js +28 -1
- package/dist/constants.js +1 -1
- package/dist/external-chat.js +83 -0
- package/dist/output.js +28 -0
- package/dist/pairing.js +1 -0
- package/package.json +1 -1
- package/skills/teamclaws-chat/SKILL.md +32 -1
package/README.md
CHANGED
|
@@ -213,6 +213,30 @@ Python helper. Use `--client-request-id <uuid>` to safely repeat an uncertain
|
|
|
213
213
|
create request, and use the watch cursors printed in JSON events to resume
|
|
214
214
|
monitoring without creating another turn.
|
|
215
215
|
|
|
216
|
+
## Read chat history without a browser
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
teamclaws chat history <target> --json
|
|
220
|
+
teamclaws chat messages <target> --chat-id <chat-public-uuid> --json
|
|
221
|
+
teamclaws chat messages <target> --chat-id <chat-public-uuid> --after-message-id <last-read-id> --json
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The first command lists chats from the workspace; the messages command reads
|
|
225
|
+
their content and later follow-ups. Both commands automatically read every page.
|
|
226
|
+
Use `chat_id` from the list or `chat_public_id` from a conversation URL. Save
|
|
227
|
+
`latest_message_id` to read only later messages next time.
|
|
228
|
+
|
|
229
|
+
This access is standard for new and existing pairings. The workspace owner's
|
|
230
|
+
agents can read every user's chats on the workspace. Other users' agents,
|
|
231
|
+
including superusers' agents, can read every chat belonging to their own user.
|
|
232
|
+
Archived, deleted, empty, and browser-created chats are included. There is no
|
|
233
|
+
optional history grant or model requirement. These commands do not create chats,
|
|
234
|
+
send messages, or mark conversations viewed. Clawgate must include the history
|
|
235
|
+
endpoints; older servers return an error rather than an empty history.
|
|
236
|
+
|
|
237
|
+
`chat list` only lists local request references. `chat watch` only reads responses
|
|
238
|
+
to a specific request, so neither command checks for later conversation turns.
|
|
239
|
+
|
|
216
240
|
## Host shell commands
|
|
217
241
|
|
|
218
242
|
For authorized workspace administration, first check pairing status with
|
package/dist/app.js
CHANGED
|
@@ -146,6 +146,10 @@ export async function run(arguments_, dependencies = {}, signal) {
|
|
|
146
146
|
}
|
|
147
147
|
case "chat_list":
|
|
148
148
|
return await externalChat.list(parsePairingTarget(parsed.target), signal);
|
|
149
|
+
case "chat_history":
|
|
150
|
+
return await externalChat.history(parsePairingTarget(parsed.target), signal);
|
|
151
|
+
case "chat_messages":
|
|
152
|
+
return await externalChat.messages(parsePairingTarget(parsed.target), parsed.chatId, parsed.afterId, signal);
|
|
149
153
|
case "chat_capabilities":
|
|
150
154
|
return await externalChat.capabilities(parsePairingTarget(parsed.target), signal);
|
|
151
155
|
case "chat_start":
|
|
@@ -257,6 +261,29 @@ export function parseArguments(arguments_) {
|
|
|
257
261
|
if (args[0] === "chat" && args[1] === "list" && args.length === 3) {
|
|
258
262
|
return { action: "chat_list", json, target: requiredPositional(args, 2) };
|
|
259
263
|
}
|
|
264
|
+
if (args[0] === "chat" && args[1] === "history" && args.length === 3) {
|
|
265
|
+
return {
|
|
266
|
+
action: "chat_history",
|
|
267
|
+
json,
|
|
268
|
+
target: requiredPositional(args, 2),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
if (args[0] === "chat" && args[1] === "messages") {
|
|
272
|
+
const target = requiredPositional(args, 2);
|
|
273
|
+
const options = parseNamedOptions(args.slice(3), {
|
|
274
|
+
values: ["--chat-id", "--after-message-id"],
|
|
275
|
+
booleans: [],
|
|
276
|
+
});
|
|
277
|
+
if (!options.values["--chat-id"])
|
|
278
|
+
throw usageError();
|
|
279
|
+
return {
|
|
280
|
+
action: "chat_messages",
|
|
281
|
+
json,
|
|
282
|
+
target,
|
|
283
|
+
chatId: normalizeUuid(options.values["--chat-id"]),
|
|
284
|
+
afterId: nonNegativeInteger(options.values["--after-message-id"]),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
260
287
|
if (args[0] === "chat" && args[1] === "capabilities" && args.length === 3) {
|
|
261
288
|
return { action: "chat_capabilities", json, target: args[2] };
|
|
262
289
|
}
|
|
@@ -471,5 +498,5 @@ function usageError() {
|
|
|
471
498
|
});
|
|
472
499
|
}
|
|
473
500
|
export function helpText() {
|
|
474
|
-
return `TeamClaws ${PACKAGE_VERSION}\n\nUsage:\n teamclaws update [--json]\n teamclaws shell <target> --command <text> [--json]\n teamclaws login [email] [--json]\n teamclaws auth status [--json]\n teamclaws logout [--revoke] [--json]\n teamclaws servers list [--json]\n teamclaws servers update <server-id> [--name <text>] [--description <text>] [--json]\n teamclaws pair status <server-identifier-or-canonical-workspace-address> [--json]\n teamclaws pair [server-identifier-or-canonical-workspace-address] [--no-open] [--json]\n teamclaws chat list <target> [--json]\n teamclaws chat capabilities <target> [--json]\n teamclaws chat start <target> --message <text> [selection options] [--json]\n teamclaws chat send <target> --session-id <uuid> --message <text> [--json]\n teamclaws chat watch <target> --session-id <uuid> --request-id <uuid> [resume cursors] [--json]\n\nPair status performs one bounded direct verification and stops. Pairing grants the standard host-issued credential. Chat commands use that credential directly against the selected workspace.\n`;
|
|
501
|
+
return `TeamClaws ${PACKAGE_VERSION}\n\nUsage:\n teamclaws update [--json]\n teamclaws shell <target> --command <text> [--json]\n teamclaws login [email] [--json]\n teamclaws auth status [--json]\n teamclaws logout [--revoke] [--json]\n teamclaws servers list [--json]\n teamclaws servers update <server-id> [--name <text>] [--description <text>] [--json]\n teamclaws pair status <server-identifier-or-canonical-workspace-address> [--json]\n teamclaws pair [server-identifier-or-canonical-workspace-address] [--no-open] [--json]\n teamclaws chat list <target> [--json]\n teamclaws chat history <target> [--json]\n teamclaws chat messages <target> --chat-id <uuid> [--after-message-id <n>] [--json]\n teamclaws chat capabilities <target> [--json]\n teamclaws chat start <target> --message <text> [selection options] [--json]\n teamclaws chat send <target> --session-id <uuid> --message <text> [--json]\n teamclaws chat watch <target> --session-id <uuid> --request-id <uuid> [resume cursors] [--json]\n\nPair status performs one bounded direct verification and stops. Pairing grants the standard host-issued credential. Chat commands use that credential directly against the selected workspace.\n`;
|
|
475
502
|
}
|
package/dist/constants.js
CHANGED
package/dist/external-chat.js
CHANGED
|
@@ -172,6 +172,84 @@ export class ExternalChatClient {
|
|
|
172
172
|
const requestId = validUuid(options.requestId);
|
|
173
173
|
return this.poll(context, { requestId, sessionId }, options.knownRevision, options.responseCursor, signal);
|
|
174
174
|
}
|
|
175
|
+
async historyCredential(target, signal) {
|
|
176
|
+
const credential = await this.store.findWorkspace(target);
|
|
177
|
+
if (!credential)
|
|
178
|
+
throw new TeamClawsError("re_pair_required", "No saved pairing was found for this workspace. Pair it first.", { exitCode: 3 });
|
|
179
|
+
const response = await this.request(`${credential.origin}/teamclaws-admin/api/device-pairings/me/`, credential, {}, [200], signal);
|
|
180
|
+
parseVerifiedDevice(response.body, credential.device_id);
|
|
181
|
+
return credential;
|
|
182
|
+
}
|
|
183
|
+
async history(target, signal) {
|
|
184
|
+
const credential = await this.historyCredential(target, signal);
|
|
185
|
+
let afterId = 0;
|
|
186
|
+
while (true) {
|
|
187
|
+
const response = await this.request(`${credential.origin}/teamclaws-admin/api/device-chat/v1/chats/?after_id=${afterId}`, credential, {}, [200], signal);
|
|
188
|
+
const body = response.body;
|
|
189
|
+
if (!isObject(body) || !Array.isArray(body.chats))
|
|
190
|
+
throw invalidChatResponse();
|
|
191
|
+
const chats = body.chats.map((chat) => {
|
|
192
|
+
if (!isObject(chat))
|
|
193
|
+
throw invalidChatResponse();
|
|
194
|
+
const url = requireHttpsUrl(chat.owner_conversation_url, "conversation address");
|
|
195
|
+
if (url.origin !== credential.origin)
|
|
196
|
+
throw invalidChatResponse();
|
|
197
|
+
return {
|
|
198
|
+
chat_id: validUuid(chat.chat_id),
|
|
199
|
+
name: historyText(chat.name),
|
|
200
|
+
surface: historyText(chat.surface),
|
|
201
|
+
created_at: historyText(chat.created_at),
|
|
202
|
+
updated_at: historyText(chat.updated_at),
|
|
203
|
+
archived_at: chat.archived_at === null ? null : historyText(chat.archived_at),
|
|
204
|
+
deleted_at: chat.deleted_at === null ? null : historyText(chat.deleted_at),
|
|
205
|
+
owner_conversation_url: url.toString(),
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
const next = body.next_after_id === null
|
|
209
|
+
? null
|
|
210
|
+
: requireInteger(body.next_after_id, "history cursor", afterId + 1, Number.MAX_SAFE_INTEGER);
|
|
211
|
+
if (next !== null && chats.length === 0)
|
|
212
|
+
throw invalidChatResponse();
|
|
213
|
+
this.output.chatHistory(chats, next);
|
|
214
|
+
if (next === null)
|
|
215
|
+
return 0;
|
|
216
|
+
afterId = next;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async messages(target, chatId, afterId, signal) {
|
|
220
|
+
chatId = validUuid(chatId);
|
|
221
|
+
const credential = await this.historyCredential(target, signal);
|
|
222
|
+
while (true) {
|
|
223
|
+
const response = await this.request(`${credential.origin}/teamclaws-admin/api/device-chat/v1/chats/${chatId}/messages/?after_id=${afterId}`, credential, {}, [200], signal);
|
|
224
|
+
const body = response.body;
|
|
225
|
+
if (!isObject(body) ||
|
|
226
|
+
body.chat_id !== chatId ||
|
|
227
|
+
!Array.isArray(body.messages) ||
|
|
228
|
+
typeof body.has_more !== "boolean")
|
|
229
|
+
throw invalidChatResponse();
|
|
230
|
+
let previousId = afterId;
|
|
231
|
+
const messages = body.messages.map((message) => {
|
|
232
|
+
if (!isObject(message))
|
|
233
|
+
throw invalidChatResponse();
|
|
234
|
+
const id = requireInteger(message.id, "message identifier", previousId + 1, Number.MAX_SAFE_INTEGER);
|
|
235
|
+
previousId = id;
|
|
236
|
+
return {
|
|
237
|
+
id,
|
|
238
|
+
role: historyText(message.role),
|
|
239
|
+
body_text: historyText(message.body_text),
|
|
240
|
+
status: historyText(message.status),
|
|
241
|
+
created_at: historyText(message.created_at),
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
const latest = requireInteger(body.latest_message_id, "message cursor", afterId, Number.MAX_SAFE_INTEGER);
|
|
245
|
+
if (latest !== previousId || (body.has_more && messages.length === 0))
|
|
246
|
+
throw invalidChatResponse();
|
|
247
|
+
this.output.chatMessages(chatId, messages, latest, body.has_more);
|
|
248
|
+
if (!body.has_more)
|
|
249
|
+
return 0;
|
|
250
|
+
afterId = latest;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
175
253
|
async list(target, signal) {
|
|
176
254
|
const credential = await this.store.findWorkspace(target);
|
|
177
255
|
if (!credential)
|
|
@@ -568,6 +646,11 @@ function requireOwnerUrl(value, origin) {
|
|
|
568
646
|
function validUuid(value) {
|
|
569
647
|
return normalizeUuid(value, "identifier");
|
|
570
648
|
}
|
|
649
|
+
function historyText(value) {
|
|
650
|
+
if (typeof value !== "string")
|
|
651
|
+
throw invalidChatResponse();
|
|
652
|
+
return value;
|
|
653
|
+
}
|
|
571
654
|
function invalidChatResponse() {
|
|
572
655
|
return new TeamClawsError("invalid_external_chat_response", "The workspace returned an invalid external chat response.", { exitCode: 9 });
|
|
573
656
|
}
|
package/dist/output.js
CHANGED
|
@@ -233,6 +233,34 @@ export class Output {
|
|
|
233
233
|
? `Snapshot generated: ${generatedAt}.\n`
|
|
234
234
|
: "Generation time is not supplied by the capabilities endpoint.\n");
|
|
235
235
|
}
|
|
236
|
+
chatHistory(chats, nextAfterId) {
|
|
237
|
+
this.event({
|
|
238
|
+
schema_version: OUTPUT_SCHEMA_VERSION,
|
|
239
|
+
event: "chat_history",
|
|
240
|
+
status: "success",
|
|
241
|
+
source: "workspace",
|
|
242
|
+
chats,
|
|
243
|
+
next_after_id: nextAfterId,
|
|
244
|
+
});
|
|
245
|
+
if (!this.json)
|
|
246
|
+
for (const chat of chats)
|
|
247
|
+
this.stdout.write(`${chat.chat_id} — ${chat.name} — ${chat.owner_conversation_url}\n`);
|
|
248
|
+
}
|
|
249
|
+
chatMessages(chatId, messages, latestMessageId, hasMore) {
|
|
250
|
+
this.event({
|
|
251
|
+
schema_version: OUTPUT_SCHEMA_VERSION,
|
|
252
|
+
event: "chat_messages",
|
|
253
|
+
status: "success",
|
|
254
|
+
source: "workspace",
|
|
255
|
+
chat_id: chatId,
|
|
256
|
+
messages,
|
|
257
|
+
latest_message_id: latestMessageId,
|
|
258
|
+
has_more: hasMore,
|
|
259
|
+
});
|
|
260
|
+
if (!this.json)
|
|
261
|
+
for (const message of messages)
|
|
262
|
+
this.stdout.write(`${message.id} ${message.role}: ${message.body_text}\n`);
|
|
263
|
+
}
|
|
236
264
|
chatSessionsList(workspace, records) {
|
|
237
265
|
const sessions = records.map((record) => ({
|
|
238
266
|
session_id: record.session_id,
|
package/dist/pairing.js
CHANGED
|
@@ -153,6 +153,7 @@ export class PairingClient {
|
|
|
153
153
|
const response = await requestJson(this.fetcher, pollUrl, {
|
|
154
154
|
method: "POST",
|
|
155
155
|
body: { device_code: pending.device_code },
|
|
156
|
+
timeoutMs: Math.max(1, deadline - this.now()),
|
|
156
157
|
...(signal ? { signal } : {}),
|
|
157
158
|
});
|
|
158
159
|
if (response.status >= 200 && response.status < 300) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: teamclaws-chat
|
|
3
|
-
description:
|
|
3
|
+
description: Read workspace chat history or start, continue, and resume an external-agent TCChat through the official TeamClaws CLI after this computer is paired with the exact workspace.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# TeamClaws external chat
|
|
@@ -20,6 +20,37 @@ transport, idempotency, and durable request polling.
|
|
|
20
20
|
- Treat the newline-delimited JSON as an allowlisted protocol. Never expose raw
|
|
21
21
|
HTTP responses or provider-native identifiers.
|
|
22
22
|
|
|
23
|
+
## Read chats and follow-up messages without a browser
|
|
24
|
+
|
|
25
|
+
For conversation history, new messages, or follow-ups, use these read-only commands:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
teamclaws chat history <target> --json
|
|
29
|
+
teamclaws chat messages <target> --chat-id <chat-public-uuid> --json
|
|
30
|
+
teamclaws chat messages <target> --chat-id <chat-public-uuid> --after-message-id <last-read-id> --json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`chat history` queries the workspace and emits `chat_history` pages containing
|
|
34
|
+
`chats`, including browser-created, archived, empty, and deleted chats. Use the
|
|
35
|
+
returned `chat_id`, or the `chat_public_id` in the user's conversation URL, with
|
|
36
|
+
`chat messages`. It emits `chat_messages` pages containing every returned role
|
|
37
|
+
and message body, including owner follow-ups and subsequent agent replies.
|
|
38
|
+
Both commands fetch every page automatically. Save the final
|
|
39
|
+
`latest_message_id` to check for later messages. A successful empty messages
|
|
40
|
+
result means no messages after that cursor in that chat at the time of the read.
|
|
41
|
+
|
|
42
|
+
History access is standard for existing and new valid pairings. There is no
|
|
43
|
+
optional history permission, browser approval, model selection, or new pairing
|
|
44
|
+
step. The workspace owner's paired agents can read every user's chats on that
|
|
45
|
+
workspace. Other paired agents, including superusers' agents, can read every
|
|
46
|
+
chat belonging to their own user. The workspace enforces this distinction.
|
|
47
|
+
|
|
48
|
+
`chat list` is a local request-reference index, not a workspace chat inventory.
|
|
49
|
+
`chat watch` reads replies to one request, not later conversation turns. Do not
|
|
50
|
+
use either as evidence that a conversation has no follow-ups. Never send a
|
|
51
|
+
message, start another chat, or use the browser just to read history. An HTTP
|
|
52
|
+
error or incomplete pagination is not evidence of an empty history.
|
|
53
|
+
|
|
23
54
|
## Start one conversation
|
|
24
55
|
|
|
25
56
|
Obtain a non-empty user message, then run:
|