teamclaws 2.0.7 → 2.0.9
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 +33 -7
- package/dist/app.js +20 -2
- package/dist/constants.js +1 -1
- package/dist/external-chat.js +23 -2
- package/dist/output.js +19 -0
- package/dist/pairing.js +9 -5
- package/dist/users.js +72 -0
- package/package.json +3 -1
- package/scripts/check-node.mjs +8 -0
- package/skills/teamclaws-chat/SKILL.md +6 -0
- package/skills/teamclaws-login/SKILL.md +3 -2
- package/skills/teamclaws-onboard/SKILL.md +3 -1
- package/skills/teamclaws-pair/SKILL.md +7 -5
package/README.md
CHANGED
|
@@ -18,10 +18,11 @@ workspace user; user membership is a separate privilege.
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
20
20
|
Check `node --version` first. If it is below 22, upgrade Node.js to version 22
|
|
21
|
-
or newer before installing.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Node.js below 22.
|
|
21
|
+
or newer before installing. The install script rejects Node.js below 22 during
|
|
22
|
+
an ordinary npm install. npm's `--ignore-scripts` or an installer policy that
|
|
23
|
+
disables lifecycle scripts bypasses this install check; the CLI still refuses
|
|
24
|
+
to run on Node.js below 22. `--engine-strict` also enforces the declared engine
|
|
25
|
+
requirement without changing global npm settings.
|
|
25
26
|
|
|
26
27
|
Install the published CLI:
|
|
27
28
|
|
|
@@ -54,12 +55,16 @@ the existing paired shell. Status-only requests do not start onboarding.
|
|
|
54
55
|
|
|
55
56
|
Before any browser authorization, the agent explains the process: sign this
|
|
56
57
|
computer's CLI into the user's TeamClaws account through the browser, choose a
|
|
57
|
-
workspace and
|
|
58
|
+
workspace and pair with that server, then choose useful
|
|
58
59
|
work and set up its environment on the server. It explains the purpose and access
|
|
59
60
|
of each approval before presenting a link, adapting to steps already completed.
|
|
60
61
|
The conversation starts with that context rather than an unexplained request to
|
|
61
62
|
click an authorization link.
|
|
62
63
|
|
|
64
|
+
Pairing completes automatically when TeamClawsDeploy confirms the account owns
|
|
65
|
+
the workspace or is a superuser in its access group. Browser approval is shown
|
|
66
|
+
only when the target reports that approval is pending.
|
|
67
|
+
|
|
63
68
|
Agents keep their turn and the original login/pairing process alive while the
|
|
64
69
|
user approves in the browser, monitor its output, and continue automatically
|
|
65
70
|
after terminal success. No "I approved" message is needed. An execution
|
|
@@ -88,7 +93,8 @@ Existing event names and schema version remain unchanged. The
|
|
|
88
93
|
login keeps running. Signed-out `auth_status` includes the non-executing
|
|
89
94
|
`login_command: "teamclaws login --json"`; status never starts device authorization.
|
|
90
95
|
|
|
91
|
-
`pairing_browser_required` supplies the safe approval
|
|
96
|
+
When approval is pending, `pairing_browser_required` supplies the safe approval
|
|
97
|
+
URL and public `user_code`. Automatic pairing emits no browser approval event.
|
|
92
98
|
Some hosts include a validated `agent_action` with the legacy type
|
|
93
99
|
`deliver_and_stop`. The agent displays its approval message and continues
|
|
94
100
|
monitoring the original process; that field does not end the agent's turn. The
|
|
@@ -239,6 +245,19 @@ acceptance. Existing memberships and pending invitations are preserved without
|
|
|
239
245
|
promotion or resend. If a request has an uncertain network outcome, inspect the
|
|
240
246
|
invitation status before trying again; the command does not blindly retry it.
|
|
241
247
|
|
|
248
|
+
### Inspect users and invitation status
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
teamclaws users list <target> --json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
This read requires the current machine owner's active paired authorization.
|
|
255
|
+
It lists current members separately from pending and accepted invitation
|
|
256
|
+
records. An accepted invitation is historical evidence of acceptance; current
|
|
257
|
+
membership determines whether the person still has access. An expired pending
|
|
258
|
+
invitation is marked expired. Listing sends no invitations and changes no roles,
|
|
259
|
+
memberships, or invitation state. It requires a workspace with the users-list API.
|
|
260
|
+
|
|
242
261
|
## Read chat history without a browser
|
|
243
262
|
|
|
244
263
|
```text
|
|
@@ -267,7 +286,8 @@ to a specific request, so neither command checks for later conversation turns.
|
|
|
267
286
|
|
|
268
287
|
For authorized workspace administration, first check pairing status with
|
|
269
288
|
`teamclaws pair status <target> --json`. If `not_paired`, complete CLI login and pairing
|
|
270
|
-
|
|
289
|
+
for that workspace before using the shell, following browser approval only when
|
|
290
|
+
requested by the CLI. A linked AI provider or
|
|
271
291
|
TCChat conversation is not required for paired-shell access; remote chat is a
|
|
272
292
|
separate operation.
|
|
273
293
|
|
|
@@ -306,6 +326,12 @@ access is enforced by TeamClaws servers, not by the client license.
|
|
|
306
326
|
|
|
307
327
|
### Provider and model capabilities
|
|
308
328
|
|
|
329
|
+
If the workspace confirms `no_provider_paired`, open its `/teamclaws-admin/`
|
|
330
|
+
page, choose **Link AI Account**, and connect a provider. For Cursor, choose
|
|
331
|
+
**Cursor**, then **My Subscription**, and complete browser authorization.
|
|
332
|
+
An unavailable provider status or a transport failure does not prove that no
|
|
333
|
+
provider is paired.
|
|
334
|
+
|
|
309
335
|
Run `teamclaws chat capabilities <target> [--json]` with an existing saved
|
|
310
336
|
workspace pairing. It reads the workspace capabilities endpoint with a shared
|
|
311
337
|
120-second budget, attempts lasting at most 15 seconds, and pauses of up to five
|
package/dist/app.js
CHANGED
|
@@ -11,7 +11,7 @@ import { CredentialStore } from "./storage.js";
|
|
|
11
11
|
import { TargetAuthorization } from "./target-authorization.js";
|
|
12
12
|
import { installRelease } from "./update.js";
|
|
13
13
|
import { normalizeUuid, parsePairingTarget } from "./validation.js";
|
|
14
|
-
import { runUsersAdd } from "./users.js";
|
|
14
|
+
import { runUsersAdd, runUsersList } from "./users.js";
|
|
15
15
|
export async function run(arguments_, dependencies = {}, signal) {
|
|
16
16
|
let parsed;
|
|
17
17
|
try {
|
|
@@ -71,6 +71,18 @@ export async function run(arguments_, dependencies = {}, signal) {
|
|
|
71
71
|
}
|
|
72
72
|
const store = dependencies.store ?? new CredentialStore();
|
|
73
73
|
const targetAuthorization = new TargetAuthorization(store, dependencies.fetcher);
|
|
74
|
+
if (parsed.action === "users_list") {
|
|
75
|
+
try {
|
|
76
|
+
await targetAuthorization.requireGrant();
|
|
77
|
+
output.usersListResult(await runUsersList(store, dependencies.fetcher ?? fetch, parsed.target, targetAuthorization, signal));
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const safe = safeError(error);
|
|
82
|
+
output.error(safe);
|
|
83
|
+
return safe.exitCode;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
74
86
|
if (parsed.action === "users_add") {
|
|
75
87
|
try {
|
|
76
88
|
await targetAuthorization.requireGrant();
|
|
@@ -231,6 +243,12 @@ export function parseArguments(arguments_) {
|
|
|
231
243
|
}
|
|
232
244
|
const json = arguments_.includes("--json");
|
|
233
245
|
const args = arguments_.filter((argument) => argument !== "--json");
|
|
246
|
+
if (args[0] === "users" && args[1] === "list" && args.length === 3)
|
|
247
|
+
return {
|
|
248
|
+
action: "users_list",
|
|
249
|
+
json,
|
|
250
|
+
target: parsePairingTarget(requiredPositional(args, 2)),
|
|
251
|
+
};
|
|
234
252
|
if (args[0] === "users" && args[1] === "add") {
|
|
235
253
|
const target = parsePairingTarget(requiredPositional(args, 2));
|
|
236
254
|
const email = requiredPositional(args, 3).trim().toLowerCase();
|
|
@@ -532,5 +550,5 @@ function usageError() {
|
|
|
532
550
|
});
|
|
533
551
|
}
|
|
534
552
|
export function helpText() {
|
|
535
|
-
return `TeamClaws ${PACKAGE_VERSION}\n\nUsage:\n teamclaws update [--json]\n teamclaws shell <target> --command <text> [--json]\n teamclaws users add <target> <email> --role <user|staff|superuser> [--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`;
|
|
553
|
+
return `TeamClaws ${PACKAGE_VERSION}\n\nUsage:\n teamclaws update [--json]\n teamclaws shell <target> --command <text> [--json]\n teamclaws users add <target> <email> --role <user|staff|superuser> [--json]\n teamclaws users list <target> [--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`;
|
|
536
554
|
}
|
package/dist/constants.js
CHANGED
package/dist/external-chat.js
CHANGED
|
@@ -88,6 +88,11 @@ export class ExternalChatClient {
|
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
const inventory = parseCapabilityInventory(response.body);
|
|
91
|
+
if (inventory.provider_availability?.code === "no_provider_paired") {
|
|
92
|
+
if (inventory.providers.length !== 0)
|
|
93
|
+
throw invalidChatResponse();
|
|
94
|
+
throw new TeamClawsError("no_provider_paired", `No AI provider is paired for this workspace. Open ${credential.origin}/teamclaws-admin/, choose Link AI Account, and connect a provider. For Cursor, choose Cursor, then My Subscription.`, { exitCode: 5 });
|
|
95
|
+
}
|
|
91
96
|
if (this.now() >= deadline)
|
|
92
97
|
break;
|
|
93
98
|
if (inventory.providers.some((provider) => provider.models.length > 0)) {
|
|
@@ -99,7 +104,10 @@ export class ExternalChatClient {
|
|
|
99
104
|
external_chat_capabilities: inventory,
|
|
100
105
|
};
|
|
101
106
|
}
|
|
102
|
-
lastFailure =
|
|
107
|
+
lastFailure =
|
|
108
|
+
inventory.provider_availability?.code === "provider_unavailable"
|
|
109
|
+
? "AI provider status is unavailable."
|
|
110
|
+
: "The workspace returned no provider/model choices.";
|
|
103
111
|
}
|
|
104
112
|
catch (error) {
|
|
105
113
|
canceled();
|
|
@@ -121,7 +129,7 @@ export class ExternalChatClient {
|
|
|
121
129
|
await this.wait(Math.min(5_000, remaining), signal);
|
|
122
130
|
}
|
|
123
131
|
canceled();
|
|
124
|
-
throw new TeamClawsError("capabilities_unavailable", `${lastFailure}
|
|
132
|
+
throw new TeamClawsError("capabilities_unavailable", `${lastFailure} Check workspace connectivity or the provider connections at ${credential.origin}/teamclaws-admin/ and retry.`, { exitCode: 5, retryable: true });
|
|
125
133
|
}
|
|
126
134
|
async start(options, signal) {
|
|
127
135
|
const context = await this.context(options.target, signal, true);
|
|
@@ -506,8 +514,21 @@ export function parseCapabilityInventory(value) {
|
|
|
506
514
|
!isObject(value.identity) ||
|
|
507
515
|
!Array.isArray(value.approved_scopes))
|
|
508
516
|
throw invalidChatResponse();
|
|
517
|
+
let availability;
|
|
518
|
+
if (value.provider_availability !== undefined) {
|
|
519
|
+
const state = value.provider_availability;
|
|
520
|
+
if (!isObject(state) ||
|
|
521
|
+
!["ready", "no_provider_paired", "provider_unavailable"].includes(String(state.code)) ||
|
|
522
|
+
state.setup_path !== "/teamclaws-admin/")
|
|
523
|
+
throw invalidChatResponse();
|
|
524
|
+
availability = {
|
|
525
|
+
code: state.code,
|
|
526
|
+
setup_path: "/teamclaws-admin/",
|
|
527
|
+
};
|
|
528
|
+
}
|
|
509
529
|
return {
|
|
510
530
|
contract_version: 1,
|
|
531
|
+
...(availability ? { provider_availability: availability } : {}),
|
|
511
532
|
identity: {
|
|
512
533
|
alias: requireString(value.identity.alias, "external identity alias", 200),
|
|
513
534
|
},
|
package/dist/output.js
CHANGED
|
@@ -405,6 +405,25 @@ export class Output {
|
|
|
405
405
|
this.stdout.write(`${messages[result.code]}\n`);
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
|
+
usersListResult(result) {
|
|
409
|
+
this.event({
|
|
410
|
+
schema_version: OUTPUT_SCHEMA_VERSION,
|
|
411
|
+
event: "users_list_result",
|
|
412
|
+
...result,
|
|
413
|
+
});
|
|
414
|
+
if (!this.json) {
|
|
415
|
+
this.stdout.write("Current members:\n");
|
|
416
|
+
for (const member of result.members)
|
|
417
|
+
this.stdout.write(`${member.email}: ${member.role}${member.is_owner ? " (owner)" : ""}\n`);
|
|
418
|
+
if (!result.members.length)
|
|
419
|
+
this.stdout.write("No members returned.\n");
|
|
420
|
+
this.stdout.write("Invitations:\n");
|
|
421
|
+
for (const invite of result.invites)
|
|
422
|
+
this.stdout.write(`${invite.email}: ${invite.status}${invite.is_expired && invite.status === "pending" ? " (expired)" : ""} — ${invite.role}\n`);
|
|
423
|
+
if (!result.invites.length)
|
|
424
|
+
this.stdout.write("No pending or accepted invitations.\n");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
408
427
|
event(event) {
|
|
409
428
|
if (this.json)
|
|
410
429
|
this.stdout.write(`${JSON.stringify(event)}\n`);
|
package/dist/pairing.js
CHANGED
|
@@ -76,12 +76,9 @@ export class PairingClient {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
async complete(pending, deviceName, noOpen, signal) {
|
|
79
|
-
this.output.pairingBrowser(pending.verification_uri_complete, pending.user_code, pending.agent_action);
|
|
80
|
-
if (!noOpen)
|
|
81
|
-
await this.openBrowser(pending.verification_uri_complete);
|
|
82
79
|
let credential;
|
|
83
80
|
try {
|
|
84
|
-
credential = await this.poll(pending, signal);
|
|
81
|
+
credential = await this.poll(pending, noOpen, signal);
|
|
85
82
|
const grant = await this.authorization.requireGrant();
|
|
86
83
|
if (credential.pairing_id !== credential.device_id ||
|
|
87
84
|
credential.origin !== pending.target.origin ||
|
|
@@ -142,9 +139,10 @@ export class PairingClient {
|
|
|
142
139
|
void credential;
|
|
143
140
|
}
|
|
144
141
|
}
|
|
145
|
-
async poll(pending, signal) {
|
|
142
|
+
async poll(pending, noOpen, signal) {
|
|
146
143
|
const deadline = this.now() + pending.expires_in * 1_000;
|
|
147
144
|
let intervalSeconds = pending.interval;
|
|
145
|
+
let approvalShown = false;
|
|
148
146
|
const pollUrl = directTargetUrl(pending.target, pending.target.token_poll_path);
|
|
149
147
|
while (this.now() < deadline) {
|
|
150
148
|
if (signal?.aborted)
|
|
@@ -158,6 +156,12 @@ export class PairingClient {
|
|
|
158
156
|
if (response.status >= 200 && response.status < 300) {
|
|
159
157
|
if (response.status === 202) {
|
|
160
158
|
intervalSeconds = pendingInterval(response, intervalSeconds);
|
|
159
|
+
if (!approvalShown) {
|
|
160
|
+
this.output.pairingBrowser(pending.verification_uri_complete, pending.user_code, pending.agent_action);
|
|
161
|
+
approvalShown = true;
|
|
162
|
+
if (!noOpen)
|
|
163
|
+
await this.openBrowser(pending.verification_uri_complete);
|
|
164
|
+
}
|
|
161
165
|
await this.wait(intervalSeconds * 1_000, signal);
|
|
162
166
|
continue;
|
|
163
167
|
}
|
package/dist/users.js
CHANGED
|
@@ -19,6 +19,78 @@ const failureMessages = {
|
|
|
19
19
|
invite_delivery_failed: "The invitation could not be delivered. Check its status before retrying.",
|
|
20
20
|
outcome_unverified: "The invitation outcome could not be verified. Check its status before retrying.",
|
|
21
21
|
};
|
|
22
|
+
export async function runUsersList(store, fetcher, target, authorization, signal) {
|
|
23
|
+
const credential = await store.findWorkspace(target);
|
|
24
|
+
if (!credential)
|
|
25
|
+
throw new TeamClawsError("re_pair_required", "No saved pairing was found for this workspace. Pair it first.", { exitCode: 3 });
|
|
26
|
+
const response = await requestTargetJson(fetcher, authorization, `${credential.origin}/teamclaws-admin/api/device-users/v1/list/`, credential, { ...(signal ? { signal } : {}) });
|
|
27
|
+
const body = response.body;
|
|
28
|
+
if (response.status !== 200 || (isObject(body) && body.ok === false)) {
|
|
29
|
+
const messages = {
|
|
30
|
+
invalid_actor_token: failureMessages.invalid_actor_token,
|
|
31
|
+
unauthorized: failureMessages.unauthorized,
|
|
32
|
+
owner_required: "Only the current machine owner can inspect users and invitations.",
|
|
33
|
+
forbidden: "The paired identity is not authorized to inspect users and invitations.",
|
|
34
|
+
actor_machine_mismatch: failureMessages.actor_machine_mismatch,
|
|
35
|
+
actor_authorization_unavailable: failureMessages.actor_authorization_unavailable,
|
|
36
|
+
authorization_unavailable: failureMessages.authorization_unavailable,
|
|
37
|
+
machine_auth_failed: failureMessages.machine_auth_failed,
|
|
38
|
+
machine_ip_rejected: failureMessages.machine_ip_rejected,
|
|
39
|
+
};
|
|
40
|
+
const rawCode = isObject(body) ? body.code : undefined;
|
|
41
|
+
const code = typeof rawCode === "string" && Object.hasOwn(messages, rawCode)
|
|
42
|
+
? rawCode
|
|
43
|
+
: [404, 405].includes(response.status)
|
|
44
|
+
? "users_list_unavailable"
|
|
45
|
+
: "users_list_failed";
|
|
46
|
+
throw new TeamClawsError(code, messages[code] ??
|
|
47
|
+
(code === "users_list_unavailable"
|
|
48
|
+
? "User and invitation status inspection is unavailable on this workspace."
|
|
49
|
+
: "The workspace could not retrieve user and invitation status. Retry the read request."), {
|
|
50
|
+
exitCode: response.status === 401 ? 3 : response.status === 429 ? 5 : 1,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const invalid = () => new TeamClawsError("invalid_response", "The workspace returned invalid user or invitation status.", { exitCode: 9 });
|
|
54
|
+
if (!isObject(body) ||
|
|
55
|
+
body.ok !== true ||
|
|
56
|
+
body.action !== "list" ||
|
|
57
|
+
body.scope !== "access_group" ||
|
|
58
|
+
!Array.isArray(body.members) ||
|
|
59
|
+
!Array.isArray(body.invites))
|
|
60
|
+
throw invalid();
|
|
61
|
+
const validEmail = (value) => typeof value === "string" &&
|
|
62
|
+
value.length > 0 &&
|
|
63
|
+
value.length <= 254 &&
|
|
64
|
+
![...value].some((character) => character.charCodeAt(0) < 0x20 || character.charCodeAt(0) === 0x7f);
|
|
65
|
+
const roles = ["user", "staff", "superuser"];
|
|
66
|
+
const members = body.members.map((row) => {
|
|
67
|
+
if (!isObject(row) ||
|
|
68
|
+
!validEmail(row.email) ||
|
|
69
|
+
!roles.includes(String(row.role)) ||
|
|
70
|
+
typeof row.is_owner !== "boolean")
|
|
71
|
+
throw invalid();
|
|
72
|
+
return {
|
|
73
|
+
email: row.email,
|
|
74
|
+
role: row.role,
|
|
75
|
+
is_owner: row.is_owner,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
const invites = body.invites.map((row) => {
|
|
79
|
+
if (!isObject(row) ||
|
|
80
|
+
!validEmail(row.email) ||
|
|
81
|
+
!roles.includes(String(row.role)) ||
|
|
82
|
+
!["pending", "accepted"].includes(String(row.status)) ||
|
|
83
|
+
typeof row.is_expired !== "boolean")
|
|
84
|
+
throw invalid();
|
|
85
|
+
return {
|
|
86
|
+
email: row.email,
|
|
87
|
+
role: row.role,
|
|
88
|
+
status: row.status,
|
|
89
|
+
is_expired: row.is_expired,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
return { ok: true, action: "list", scope: "access_group", members, invites };
|
|
93
|
+
}
|
|
22
94
|
export async function runUsersAdd(store, fetcher, target, email, role, authorization, signal) {
|
|
23
95
|
const credential = await store.findWorkspace(target);
|
|
24
96
|
if (!credential)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teamclaws",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "Official TeamClaws command-line client and agent skills",
|
|
5
5
|
"license": "PolyForm-Shield-1.0.0",
|
|
6
6
|
"homepage": "https://teamclaws.com",
|
|
@@ -17,12 +17,14 @@
|
|
|
17
17
|
"teamclaws": "./dist/cli.js"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
+
"scripts/check-node.mjs",
|
|
20
21
|
"dist/",
|
|
21
22
|
"skills/",
|
|
22
23
|
"LICENSE",
|
|
23
24
|
"README.md"
|
|
24
25
|
],
|
|
25
26
|
"scripts": {
|
|
27
|
+
"preinstall": "node scripts/check-node.mjs",
|
|
26
28
|
"build": "tsc -p tsconfig.build.json",
|
|
27
29
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
28
30
|
"lint": "eslint .",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
|
|
3
|
+
if (Number(process.versions.node.split(".")[0]) < 22) {
|
|
4
|
+
process.stderr.write(
|
|
5
|
+
`TeamClaws requires Node.js 22 or newer; this runtime is ${process.versions.node}. Upgrade Node.js before installing TeamClaws.\n`,
|
|
6
|
+
);
|
|
7
|
+
process.exitCode = 1;
|
|
8
|
+
}
|
|
@@ -51,6 +51,12 @@ use either as evidence that a conversation has no follow-ups. Never send a
|
|
|
51
51
|
message, start another chat, or use the browser just to read history. An HTTP
|
|
52
52
|
error or incomplete pagination is not evidence of an empty history.
|
|
53
53
|
|
|
54
|
+
On `no_provider_paired`, explain that no AI provider is paired for this workspace.
|
|
55
|
+
Open the verified workspace's `/teamclaws-admin/` page and choose **Link AI
|
|
56
|
+
Account**. For Cursor, choose **Cursor**, then **My Subscription**, and complete
|
|
57
|
+
browser authorization. Do not describe an unavailable inventory or a transport
|
|
58
|
+
failure as proof that no provider is paired.
|
|
59
|
+
|
|
54
60
|
## Start one conversation
|
|
55
61
|
|
|
56
62
|
Obtain a non-empty user message, then run:
|
|
@@ -11,8 +11,9 @@ A status-only request runs only `teamclaws auth status --json` and stops.
|
|
|
11
11
|
|
|
12
12
|
Before starting browser login, explain that the user will sign this computer's
|
|
13
13
|
TeamClaws command-line client into their TeamClaws account through the browser.
|
|
14
|
-
This connects the client to their account; workspace selection and
|
|
15
|
-
|
|
14
|
+
This connects the client to their account; workspace selection and pairing come
|
|
15
|
+
next when they are connecting a workspace. Pairing may complete automatically;
|
|
16
|
+
show browser approval only if the CLI requests it. If onboarding
|
|
16
17
|
already explained the process, use a short transition into this step. Give the
|
|
17
18
|
approval link with that context, never as an unexplained authorization request.
|
|
18
19
|
|
|
@@ -22,7 +22,9 @@ user's work and any steps already completed:
|
|
|
22
22
|
1. First, we'll sign this computer into your TeamClaws account using your browser.
|
|
23
23
|
This lets the TeamClaws command-line client here connect to your account.
|
|
24
24
|
2. Next, you'll choose a workspace, and we'll pair this computer with that server.
|
|
25
|
-
|
|
25
|
+
Pairing completes automatically when TeamClawsDeploy confirms you own the
|
|
26
|
+
workspace or are a superuser in its access group. Follow browser approval
|
|
27
|
+
only if the CLI requests it. Pairing gives this computer
|
|
26
28
|
access to run commands and work with files on the workspace.
|
|
27
29
|
3. Then we'll find the best work to move there and set up a clean environment for
|
|
28
30
|
it, separate from your laptop. That could be a complete job, an agent, a service
|
|
@@ -56,7 +56,7 @@ teamclaws pair <stable-server-identifier> --json
|
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
Use `--no-open` only when automatic browser opening is undesirable. The
|
|
59
|
-
`pairing_browser_required` event
|
|
59
|
+
`pairing_browser_required` event provides the safe approval address when approval is needed.
|
|
60
60
|
|
|
61
61
|
When no selected stable identifier is known, run `teamclaws servers list --json`,
|
|
62
62
|
show each server's display name (`alias`), returned `description`, and readiness,
|
|
@@ -71,11 +71,13 @@ workspaces; use the onboarding skill's discovery rules.
|
|
|
71
71
|
|
|
72
72
|
## Wait for approval and continue automatically
|
|
73
73
|
|
|
74
|
-
Before starting the command, explain that
|
|
75
|
-
|
|
74
|
+
Before starting the command, explain that pairing connects this computer with
|
|
75
|
+
the selected workspace. TeamClawsDeploy authorizes automatic pairing for its
|
|
76
|
+
owner or a superuser in the workspace's access group. Follow browser approval
|
|
77
|
+
only if the CLI requests it. Name that workspace and explain that the
|
|
76
78
|
connection gives this computer access to run commands and work with its files.
|
|
77
79
|
Connect the step to the user's intended work, and say you'll keep waiting and
|
|
78
|
-
continue automatically after
|
|
80
|
+
continue automatically after pairing. Use a short transition if onboarding
|
|
79
81
|
already introduced the process; the approval link should never arrive without
|
|
80
82
|
an explanation of what the user is authorizing.
|
|
81
83
|
|
|
@@ -145,7 +147,7 @@ teamclaws pair status <stable-server-identifier-or-canonical-workspace-address>
|
|
|
145
147
|
```
|
|
146
148
|
|
|
147
149
|
If it reports `not_paired`, follow the login and pairing flow above for that
|
|
148
|
-
selected workspace;
|
|
150
|
+
selected workspace; follow browser approval only if `pairing_browser_required` is emitted. Do not use
|
|
149
151
|
another workspace's pairing or bypass approval. After `paired` is verified,
|
|
150
152
|
authorized host commands can use the existing paired shell:
|
|
151
153
|
|