teamclaws 2.0.4 → 2.0.7
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 +26 -0
- package/dist/app.js +35 -1
- package/dist/constants.js +1 -1
- package/dist/external-chat.js +3 -2
- package/dist/machine-key.js +10 -4
- package/dist/output.js +16 -0
- package/dist/startup.js +7 -0
- package/dist/storage.js +13 -3
- package/dist/update.js +3 -1
- package/dist/users.js +78 -0
- package/package.json +1 -1
- package/skills/teamclaws-login/SKILL.md +3 -0
- package/skills/teamclaws-onboard/SKILL.md +3 -0
- package/skills/teamclaws-onboard/references/workspace-uses.md +3 -0
- package/skills/teamclaws-pair/SKILL.md +3 -0
- package/skills/teamclaws-project/SKILL.md +3 -0
package/README.md
CHANGED
|
@@ -5,6 +5,9 @@ with a TeamClaws workspace and stores the resulting Clawgate credential on that
|
|
|
5
5
|
computer. The command-line client and its agent skills ship together in this
|
|
6
6
|
package.
|
|
7
7
|
|
|
8
|
+
Pairing connects this computer to the workspace. It does not add a person as a
|
|
9
|
+
workspace user; user membership is a separate privilege.
|
|
10
|
+
|
|
8
11
|
## Requirements
|
|
9
12
|
|
|
10
13
|
- Node.js 22 or newer
|
|
@@ -14,6 +17,12 @@ package.
|
|
|
14
17
|
|
|
15
18
|
## Installation
|
|
16
19
|
|
|
20
|
+
Check `node --version` first. If it is below 22, upgrade Node.js to version 22
|
|
21
|
+
or newer before installing. npm normally treats the package engine requirement
|
|
22
|
+
as a warning; `npm install -g teamclaws@latest --engine-strict` rejects an
|
|
23
|
+
unsupported Node.js version during installation. The CLI also refuses to run on
|
|
24
|
+
Node.js below 22.
|
|
25
|
+
|
|
17
26
|
Install the published CLI:
|
|
18
27
|
|
|
19
28
|
```text
|
|
@@ -172,6 +181,7 @@ npx teamclaws pair status <server-identifier-or-canonical-workspace-address>
|
|
|
172
181
|
npx teamclaws pair
|
|
173
182
|
npx teamclaws pair <server-identifier-or-canonical-workspace-address> [--no-open]
|
|
174
183
|
npx teamclaws shell <target> --command <text> [--json]
|
|
184
|
+
npx teamclaws users add <target> <email> --role <user|staff|superuser> [--json]
|
|
175
185
|
npx teamclaws chat start <target> --message <text>
|
|
176
186
|
npx teamclaws chat send <target> --session-id <uuid> --message <text>
|
|
177
187
|
npx teamclaws chat watch <target> --session-id <uuid> --request-id <uuid>
|
|
@@ -213,6 +223,22 @@ Python helper. Use `--client-request-id <uuid>` to safely repeat an uncertain
|
|
|
213
223
|
create request, and use the watch cursors printed in JSON events to resume
|
|
214
224
|
monitoring without creating another turn.
|
|
215
225
|
|
|
226
|
+
## Add a workspace user directly
|
|
227
|
+
|
|
228
|
+
`teamclaws users add <target> <email> --role user --json` sends a direct request
|
|
229
|
+
using the saved paired machine credential and its bound authorization. The role
|
|
230
|
+
is required; supported roles are `user`, `staff`, and `superuser`. The server
|
|
231
|
+
verifies current owner authority and the shared six-calendar-month machine
|
|
232
|
+
authorization deadline. Initial CLI login and pairing remain the normal setup.
|
|
233
|
+
|
|
234
|
+
This operation requires no agent, AI provider, chat, shell, skill, or browser
|
|
235
|
+
session. Missing or inactive credentials return an error without opening a browser.
|
|
236
|
+
The result's `requested_role` is the requested access, not a change to an existing
|
|
237
|
+
member's role. `invited` means an invitation was sent; access starts after
|
|
238
|
+
acceptance. Existing memberships and pending invitations are preserved without
|
|
239
|
+
promotion or resend. If a request has an uncertain network outcome, inspect the
|
|
240
|
+
invitation status before trying again; the command does not blindly retry it.
|
|
241
|
+
|
|
216
242
|
## Read chat history without a browser
|
|
217
243
|
|
|
218
244
|
```text
|
package/dist/app.js
CHANGED
|
@@ -11,6 +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
15
|
export async function run(arguments_, dependencies = {}, signal) {
|
|
15
16
|
let parsed;
|
|
16
17
|
try {
|
|
@@ -70,6 +71,19 @@ export async function run(arguments_, dependencies = {}, signal) {
|
|
|
70
71
|
}
|
|
71
72
|
const store = dependencies.store ?? new CredentialStore();
|
|
72
73
|
const targetAuthorization = new TargetAuthorization(store, dependencies.fetcher);
|
|
74
|
+
if (parsed.action === "users_add") {
|
|
75
|
+
try {
|
|
76
|
+
await targetAuthorization.requireGrant();
|
|
77
|
+
const result = await runUsersAdd(store, dependencies.fetcher ?? fetch, parsed.target, parsed.email, parsed.role, targetAuthorization, signal);
|
|
78
|
+
output.usersAddResult(result);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const safe = safeError(error);
|
|
83
|
+
output.error(safe);
|
|
84
|
+
return safe.exitCode;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
73
87
|
const auth = new AuthService({
|
|
74
88
|
store,
|
|
75
89
|
output,
|
|
@@ -217,6 +231,26 @@ export function parseArguments(arguments_) {
|
|
|
217
231
|
}
|
|
218
232
|
const json = arguments_.includes("--json");
|
|
219
233
|
const args = arguments_.filter((argument) => argument !== "--json");
|
|
234
|
+
if (args[0] === "users" && args[1] === "add") {
|
|
235
|
+
const target = parsePairingTarget(requiredPositional(args, 2));
|
|
236
|
+
const email = requiredPositional(args, 3).trim().toLowerCase();
|
|
237
|
+
const options = parseNamedOptions(args.slice(4), {
|
|
238
|
+
values: ["--role"],
|
|
239
|
+
booleans: [],
|
|
240
|
+
});
|
|
241
|
+
const role = options.values["--role"];
|
|
242
|
+
if (!email ||
|
|
243
|
+
email.length > 254 ||
|
|
244
|
+
!["user", "staff", "superuser"].includes(role ?? ""))
|
|
245
|
+
throw usageError();
|
|
246
|
+
return {
|
|
247
|
+
action: "users_add",
|
|
248
|
+
json,
|
|
249
|
+
target,
|
|
250
|
+
email,
|
|
251
|
+
role: role,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
220
254
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h")
|
|
221
255
|
return { action: "help", json };
|
|
222
256
|
if (args[0] === "--version" || args[0] === "-v")
|
|
@@ -498,5 +532,5 @@ function usageError() {
|
|
|
498
532
|
});
|
|
499
533
|
}
|
|
500
534
|
export function helpText() {
|
|
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`;
|
|
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`;
|
|
502
536
|
}
|
package/dist/constants.js
CHANGED
package/dist/external-chat.js
CHANGED
|
@@ -63,7 +63,7 @@ export class ExternalChatClient {
|
|
|
63
63
|
const remainingAttempt = deadline - this.now();
|
|
64
64
|
if (remainingAttempt < 1)
|
|
65
65
|
break;
|
|
66
|
-
const timeoutMs = Math.max(1, Math.floor(Math.min(
|
|
66
|
+
const timeoutMs = Math.max(1, Math.floor(Math.min(60_000, remainingAttempt)));
|
|
67
67
|
const timeout = AbortSignal.timeout(timeoutMs);
|
|
68
68
|
const attemptSignal = signal
|
|
69
69
|
? AbortSignal.any([timeout, signal])
|
|
@@ -297,7 +297,7 @@ export class ExternalChatClient {
|
|
|
297
297
|
capabilities: parseCapabilities(result.external_chat_capabilities, true),
|
|
298
298
|
};
|
|
299
299
|
}
|
|
300
|
-
const capabilityResponse = await this.request(new URL(handoff.capabilities_url, `${credential.origin}/`).toString(), credential, {}, [200], signal);
|
|
300
|
+
const capabilityResponse = await this.request(new URL(handoff.capabilities_url, `${credential.origin}/`).toString(), credential, { timeoutMs: 60_000 }, [200], signal);
|
|
301
301
|
return {
|
|
302
302
|
credential,
|
|
303
303
|
capabilities: parseCapabilities(capabilityResponse.body),
|
|
@@ -375,6 +375,7 @@ export class ExternalChatClient {
|
|
|
375
375
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
376
376
|
try {
|
|
377
377
|
const response = await requestTargetJson(this.fetcher, this.authorization, url, credential, {
|
|
378
|
+
...(options.method === "POST" ? { timeoutMs: 60_000 } : {}),
|
|
378
379
|
...options,
|
|
379
380
|
headers: {
|
|
380
381
|
...options.headers,
|
package/dist/machine-key.js
CHANGED
|
@@ -73,7 +73,9 @@ export class OsMachineKeyProvider {
|
|
|
73
73
|
await assertPrivateDirectory(directory);
|
|
74
74
|
return directory;
|
|
75
75
|
}
|
|
76
|
-
catch {
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error instanceof TeamClawsError)
|
|
78
|
+
throw error;
|
|
77
79
|
throw keyUnavailable();
|
|
78
80
|
}
|
|
79
81
|
}
|
|
@@ -158,7 +160,9 @@ export class OsMachineKeyProvider {
|
|
|
158
160
|
dsaEncoding: "ieee-p1363",
|
|
159
161
|
}).toString("base64url");
|
|
160
162
|
}
|
|
161
|
-
catch {
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (error instanceof TeamClawsError)
|
|
165
|
+
throw error;
|
|
162
166
|
throw keyUnavailable();
|
|
163
167
|
}
|
|
164
168
|
}
|
|
@@ -167,9 +171,11 @@ async function assertPrivateDirectory(path) {
|
|
|
167
171
|
const info = await lstat(path);
|
|
168
172
|
if (!info.isDirectory() ||
|
|
169
173
|
info.isSymbolicLink() ||
|
|
170
|
-
info.uid !== process.getuid?.()
|
|
171
|
-
(info.mode & 0o777) !== 0o700)
|
|
174
|
+
info.uid !== process.getuid?.())
|
|
172
175
|
throw keyUnavailable();
|
|
176
|
+
const mode = info.mode & 0o777;
|
|
177
|
+
if (mode !== 0o700)
|
|
178
|
+
throw new TeamClawsError("machine_key_unavailable", `The machine-key directory ${JSON.stringify(path)} has mode ${mode.toString(8).padStart(4, "0")}; mode 0700 is required. Run chmod 700 '${path.replaceAll("'", "'\\''")}' and retry.`, { exitCode: 9 });
|
|
173
179
|
}
|
|
174
180
|
async function assertPrivateFile(path) {
|
|
175
181
|
const info = await lstat(path);
|
package/dist/output.js
CHANGED
|
@@ -389,6 +389,22 @@ export class Output {
|
|
|
389
389
|
else
|
|
390
390
|
this.stderr.write(`Error: ${error.message}\n`);
|
|
391
391
|
}
|
|
392
|
+
usersAddResult(result) {
|
|
393
|
+
this.event({
|
|
394
|
+
schema_version: OUTPUT_SCHEMA_VERSION,
|
|
395
|
+
event: "users_add_result",
|
|
396
|
+
...result,
|
|
397
|
+
});
|
|
398
|
+
if (!this.json) {
|
|
399
|
+
const messages = {
|
|
400
|
+
invited: `Invitation sent for ${result.requested_role} access. Access begins after acceptance.`,
|
|
401
|
+
already_pending: "An invitation is already pending; it was not resent.",
|
|
402
|
+
already_member: "Already a member; access unchanged.",
|
|
403
|
+
already_owner: "Already the owner; access unchanged.",
|
|
404
|
+
};
|
|
405
|
+
this.stdout.write(`${messages[result.code]}\n`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
392
408
|
event(event) {
|
|
393
409
|
if (this.json)
|
|
394
410
|
this.stdout.write(`${JSON.stringify(event)}\n`);
|
package/dist/startup.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { parseArguments, run } from "./app.js";
|
|
2
2
|
import { checkRelease, releaseNotice } from "./update.js";
|
|
3
|
+
import { TeamClawsError } from "./errors.js";
|
|
4
|
+
import { Output } from "./output.js";
|
|
3
5
|
export async function runCli(args, dependencies = {}, signal, check = checkRelease) {
|
|
6
|
+
if (Number(process.versions.node.split(".")[0]) < 22) {
|
|
7
|
+
const error = new TeamClawsError("unsupported_runtime", `TeamClaws requires Node.js 22 or newer; this runtime is ${process.versions.node}. Upgrade Node.js, then run npm install -g teamclaws@latest.`, { exitCode: 9 });
|
|
8
|
+
new Output(args.includes("--json"), dependencies.io?.stdout, dependencies.io?.stderr).error(error);
|
|
9
|
+
return error.exitCode;
|
|
10
|
+
}
|
|
4
11
|
// No persistent cache: every executable invocation checks before dispatch.
|
|
5
12
|
let releaseCheck;
|
|
6
13
|
try {
|
package/dist/storage.js
CHANGED
|
@@ -118,6 +118,13 @@ export class CredentialStore {
|
|
|
118
118
|
dirname(directory),
|
|
119
119
|
directory,
|
|
120
120
|
]) {
|
|
121
|
+
const info = await lstat(path);
|
|
122
|
+
if (this.platform !== "win32" &&
|
|
123
|
+
info.isDirectory() &&
|
|
124
|
+
!info.isSymbolicLink() &&
|
|
125
|
+
info.uid === process.getuid?.() &&
|
|
126
|
+
(info.mode & 0o077) !== 0)
|
|
127
|
+
throw conversationStorageError(`Directory ${JSON.stringify(path)} has mode ${(info.mode & 0o777).toString(8).padStart(4, "0")}; mode 0700 is required. Run chmod 700 '${path.replaceAll("'", "'\\''")}' and retry. `);
|
|
121
128
|
await this.assertPrivateDirectory(path);
|
|
122
129
|
}
|
|
123
130
|
}
|
|
@@ -150,7 +157,10 @@ export class CredentialStore {
|
|
|
150
157
|
}
|
|
151
158
|
return records.sort((a, b) => b.updated_at.localeCompare(a.updated_at));
|
|
152
159
|
}
|
|
153
|
-
catch {
|
|
160
|
+
catch (error) {
|
|
161
|
+
if (error instanceof TeamClawsError &&
|
|
162
|
+
error.code === "chat_reference_storage_failed")
|
|
163
|
+
throw error;
|
|
154
164
|
throw conversationStorageError();
|
|
155
165
|
}
|
|
156
166
|
}
|
|
@@ -460,6 +470,6 @@ function parseConversationReference(value, credential) {
|
|
|
460
470
|
updated_at: value.updated_at,
|
|
461
471
|
};
|
|
462
472
|
}
|
|
463
|
-
function conversationStorageError() {
|
|
464
|
-
return new TeamClawsError("chat_reference_storage_failed",
|
|
473
|
+
function conversationStorageError(detail = "") {
|
|
474
|
+
return new TeamClawsError("chat_reference_storage_failed", `${detail}The local conversation references could not be read or saved safely. Retain any returned session/request identifiers; do not resend the message automatically.`, { exitCode: 9 });
|
|
465
475
|
}
|
package/dist/update.js
CHANGED
|
@@ -143,7 +143,9 @@ export async function npmRunner(nodeExecutable = process.execPath, platform = pr
|
|
|
143
143
|
return async (args, timeout, signal) => {
|
|
144
144
|
const env = { ...process.env };
|
|
145
145
|
for (const key of Object.keys(env)) {
|
|
146
|
-
if (/^npm_config_/i.test(key)
|
|
146
|
+
if ((/^npm_config_/i.test(key) &&
|
|
147
|
+
!(args[0] === "prefix" &&
|
|
148
|
+
/^npm_config_(prefix|userconfig|globalconfig)$/i.test(key))) ||
|
|
147
149
|
/^(NODE_OPTIONS|NODE_PATH)$/i.test(key))
|
|
148
150
|
delete env[key];
|
|
149
151
|
}
|
package/dist/users.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { TeamClawsError } from "./errors.js";
|
|
2
|
+
import { requestTargetJson, } from "./target-authorization.js";
|
|
3
|
+
import { isObject } from "./validation.js";
|
|
4
|
+
const failureMessages = {
|
|
5
|
+
invalid_actor_token: "The machine authorization is invalid, expired, paused, or revoked.",
|
|
6
|
+
unauthorized: "The workspace rejected the paired machine credential.",
|
|
7
|
+
target_proof_expired: "The request proof expired before user addition.",
|
|
8
|
+
forbidden: "The paired identity is not authorized to add users.",
|
|
9
|
+
owner_required: "Only the current machine owner can add users.",
|
|
10
|
+
actor_machine_mismatch: "The authorization does not match this machine or its paired identity.",
|
|
11
|
+
actor_authorization_unavailable: "Machine authorization could not be verified. Try again later.",
|
|
12
|
+
authorization_unavailable: "Machine authorization could not be verified. Try again later.",
|
|
13
|
+
machine_auth_failed: "The workspace's machine authentication was rejected.",
|
|
14
|
+
machine_ip_rejected: "The workspace's machine address was rejected.",
|
|
15
|
+
mfa_required: "The owner must meet the existing MFA policy before adding users.",
|
|
16
|
+
invalid_email: "Enter a valid email address.",
|
|
17
|
+
invalid_role: "Choose user, staff, or superuser access.",
|
|
18
|
+
invalid_request: "The workspace rejected the add-user request.",
|
|
19
|
+
invite_delivery_failed: "The invitation could not be delivered. Check its status before retrying.",
|
|
20
|
+
outcome_unverified: "The invitation outcome could not be verified. Check its status before retrying.",
|
|
21
|
+
};
|
|
22
|
+
export async function runUsersAdd(store, fetcher, target, email, role, 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
|
+
let response;
|
|
27
|
+
try {
|
|
28
|
+
response = await requestTargetJson(fetcher, authorization, `${credential.origin}/teamclaws-admin/api/device-users/v1/add/`, credential, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
body: { email, role },
|
|
31
|
+
timeoutMs: 60_000,
|
|
32
|
+
...(signal ? { signal } : {}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof TeamClawsError &&
|
|
37
|
+
["network_error", "network_timeout"].includes(error.code))
|
|
38
|
+
throw new TeamClawsError("outcome_unverified", failureMessages.outcome_unverified);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
const body = response.body;
|
|
42
|
+
if (response.status < 200 ||
|
|
43
|
+
response.status >= 300 ||
|
|
44
|
+
(isObject(body) && body.ok === false)) {
|
|
45
|
+
const rawCode = isObject(body)
|
|
46
|
+
? (body.code ?? (isObject(body.error) ? body.error.code : body.error))
|
|
47
|
+
: undefined;
|
|
48
|
+
const code = typeof rawCode === "string" && Object.hasOwn(failureMessages, rawCode)
|
|
49
|
+
? rawCode
|
|
50
|
+
: response.status === 404 || response.status === 405
|
|
51
|
+
? "users_add_unavailable"
|
|
52
|
+
: "outcome_unverified";
|
|
53
|
+
throw new TeamClawsError(code, failureMessages[code] ??
|
|
54
|
+
(code === "users_add_unavailable"
|
|
55
|
+
? "Direct user addition is unavailable on this workspace."
|
|
56
|
+
: "The workspace could not verify the add-user result."), {
|
|
57
|
+
exitCode: response.status === 401 ? 3 : response.status === 429 ? 5 : 1,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (!isObject(body) ||
|
|
61
|
+
body.ok !== true ||
|
|
62
|
+
body.action !== "add" ||
|
|
63
|
+
body.scope !== "access_group" ||
|
|
64
|
+
body.email !== email ||
|
|
65
|
+
body.requested_role !== role ||
|
|
66
|
+
!((response.status === 201 && body.code === "invited") ||
|
|
67
|
+
(response.status === 200 &&
|
|
68
|
+
["already_pending", "already_member", "already_owner"].includes(String(body.code)))))
|
|
69
|
+
throw new TeamClawsError("invalid_response", "The workspace returned an invalid add-user result. Check the invitation status before retrying.", { exitCode: 9 });
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
code: body.code,
|
|
73
|
+
action: "add",
|
|
74
|
+
scope: "access_group",
|
|
75
|
+
email,
|
|
76
|
+
requested_role: role,
|
|
77
|
+
};
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -16,6 +16,9 @@ pairing approval come next when they are connecting a workspace. If onboarding
|
|
|
16
16
|
already explained the process, use a short transition into this step. Give the
|
|
17
17
|
approval link with that context, never as an unexplained authorization request.
|
|
18
18
|
|
|
19
|
+
Pairing connects this computer to the workspace. It does not add a person as a
|
|
20
|
+
workspace user; user membership is a separate privilege.
|
|
21
|
+
|
|
19
22
|
Use the official executable:
|
|
20
23
|
|
|
21
24
|
```text
|
|
@@ -37,6 +37,9 @@ or add another permission question for an already authorized step. Describe the
|
|
|
37
37
|
separate server environment accurately; inspect an existing workspace before
|
|
38
38
|
promising it is empty or that a particular isolation mechanism is configured.
|
|
39
39
|
|
|
40
|
+
Pairing connects this computer to the workspace. It does not add a person as a
|
|
41
|
+
workspace user; user membership is a separate privilege.
|
|
42
|
+
|
|
40
43
|
## Sign in
|
|
41
44
|
|
|
42
45
|
Run `teamclaws auth status --json`. If signed in, confirm the returned account
|
|
@@ -122,6 +122,9 @@ and the selected access behavior before providing a working URL.
|
|
|
122
122
|
|
|
123
123
|
## Share work with other people
|
|
124
124
|
|
|
125
|
+
Pairing connects this computer to the workspace. It does not add a person as a
|
|
126
|
+
workspace user; user membership is a separate privilege.
|
|
127
|
+
|
|
125
128
|
Distinguish inviting someone to a workspace from sharing a project's web
|
|
126
129
|
address. Workspace collaboration depends on the supported TeamClaws sharing
|
|
127
130
|
controls and the recipient's assigned access. A public app URL is available
|
|
@@ -11,6 +11,9 @@ For the full install-and-connect conversation, follow
|
|
|
11
11
|
Use the official `teamclaws` executable. Do not implement pairing inside this
|
|
12
12
|
skill.
|
|
13
13
|
|
|
14
|
+
Pairing connects this computer to the workspace. It does not add a person as a
|
|
15
|
+
workspace user; user membership is a separate privilege.
|
|
16
|
+
|
|
14
17
|
## Required boundary
|
|
15
18
|
|
|
16
19
|
- If workspace discovery was performed through a model context protocol client,
|
|
@@ -20,6 +20,9 @@ carry that task through instead of restarting a generic onboarding conversation.
|
|
|
20
20
|
Inspect the destination's instructions, available runtimes, resources, network
|
|
21
21
|
access, and account permissions through the paired shell before choosing a setup.
|
|
22
22
|
|
|
23
|
+
Pairing connects this computer to the workspace. It does not add a person as a
|
|
24
|
+
workspace user; user membership is a separate privilege.
|
|
25
|
+
|
|
23
26
|
## Jobs, services, and external systems
|
|
24
27
|
|
|
25
28
|
For a complete job, place its code and authorized inputs in a dedicated directory,
|