@canonmsg/claude-code-plugin 0.29.0 → 0.29.1
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/.claude-plugin/plugin.json +1 -1
- package/README.md +44 -8
- package/dist/host.js +2 -7
- package/dist/session-state.d.ts +9 -22
- package/dist/session-state.js +7 -23
- package/package.json +6 -6
- package/skills/configure/SKILL.md +31 -22
- package/skills/register/SKILL.md +8 -3
package/README.md
CHANGED
|
@@ -4,13 +4,14 @@ Connect Claude Code to [Canon](https://github.com/HeyBobChan/canon) — a messag
|
|
|
4
4
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
|
-
The package includes a compatible Claude Code runtime. If `claude` is installed on `PATH`, or selected with `CANON_CLAUDE_CLI_PATH`, use Claude Code 2.1.220 or newer — the model picker lists whatever the CLI reports, so an older binary hides newer model families.
|
|
7
|
+
The package includes a compatible Claude Code runtime. If `claude` is installed on `PATH`, or selected with `CANON_CLAUDE_CLI_PATH`, use Claude Code 2.1.220 or newer — the model picker lists whatever the CLI reports, so an older binary hides newer model families. Either source below that minimum is logged and skipped in favour of the bundled runtime, so a stale install costs model options rather than taking the host down.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
# Install
|
|
11
11
|
npm install -g @canonmsg/claude-code-plugin
|
|
12
12
|
|
|
13
13
|
# Register (approve in Canon app when prompted)
|
|
14
|
+
export CANON_ENVIRONMENT_ID=canon-prod-v1
|
|
14
15
|
canon-register --name "My Claude" --description "My Claude Code agent" --phone "+15551234567"
|
|
15
16
|
|
|
16
17
|
# Run
|
|
@@ -38,6 +39,10 @@ Public docs: <https://canonmail.com/agents/integrations>. Coding-host concepts:
|
|
|
38
39
|
|
|
39
40
|
- **Two-way messaging** — Messages from Canon flow to Claude Code and back
|
|
40
41
|
- **Session controls** — Canon renders setup and live controls from the runtime descriptor Claude publishes
|
|
42
|
+
- **Blocking approvals** — Tool permission requests become approval cards in Canon and hold the turn until answered
|
|
43
|
+
- **Questions from Claude** — `AskUserQuestion` dialogs render as answerable cards in the conversation
|
|
44
|
+
- **Runtime commands** — `/status`, `/mcp`, `/plugins`, `/model`, `/permission`, `/effort`, `/ultracode`, `/plan`, plus the CLI's own slash commands passed through to the runtime
|
|
45
|
+
- **Canon verb tools** — Claude can act on Canon itself through an in-process verb MCP server mounted into every host session
|
|
41
46
|
- **Live preview** — See Claude's current live preview/status in the app
|
|
42
47
|
- **Interrupt** — Stop Claude mid-response from the app
|
|
43
48
|
- **Context meter** — See context window usage in the app
|
|
@@ -70,25 +75,56 @@ Current Canon truth for Claude host mode:
|
|
|
70
75
|
## Multiple agents
|
|
71
76
|
|
|
72
77
|
```bash
|
|
78
|
+
export CANON_ENVIRONMENT_ID=canon-prod-v1
|
|
73
79
|
canon-register --name "Frontend" --description "React work" --phone "+1..." --profile frontend
|
|
74
80
|
CANON_AGENT=frontend canon-claude --cwd ~/projects/frontend
|
|
75
81
|
```
|
|
76
82
|
|
|
83
|
+
With more than one registered profile, `CANON_AGENT` is required — the host refuses to guess and lists the available profiles instead.
|
|
84
|
+
|
|
77
85
|
## Channel mode (alternative)
|
|
78
86
|
|
|
79
|
-
For a lighter integration without session controls, run Canon as a channel
|
|
87
|
+
For a lighter integration without session controls, run Canon as a channel inside your own Claude Code session. The `canon-channel-server` binary (installed by this package) is an MCP stdio server that Claude Code launches as a channel.
|
|
88
|
+
|
|
89
|
+
Add it to the project's `.mcp.json` (or `~/.mcp.json` for global):
|
|
80
90
|
|
|
81
|
-
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"canon-channel": {
|
|
95
|
+
"command": "canon-channel-server"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Then start Claude Code with the channel loaded:
|
|
82
102
|
|
|
83
103
|
```bash
|
|
84
|
-
|
|
85
|
-
claude
|
|
104
|
+
CANON_API_KEY=agk_live_... CANON_ENVIRONMENT_ID=canon-prod-v1 \
|
|
105
|
+
claude --dangerously-load-development-channels server:canon-channel
|
|
106
|
+
```
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
|
|
108
|
+
The launch flag is what activates channel mode; plain `claude` treats the binary as an ordinary MCP server and never opens the channel. Inbound Canon messages then arrive as `<channel>` tags and Claude answers with the `reply` tool. Canon shows a read-only session status instead of host-mode controls, and `canon-necromance` lists the session as non-revivable.
|
|
109
|
+
|
|
110
|
+
If you registered a profile with `canon-register`, pin it instead of passing the key — the stored profile carries its own environment binding:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"canon-channel": {
|
|
116
|
+
"command": "canon-channel-server",
|
|
117
|
+
"env": { "CANON_AGENT": "my-agent" }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
89
121
|
```
|
|
90
122
|
|
|
91
|
-
|
|
123
|
+
`CANON_PLUGIN_API_KEY` is accepted wherever `CANON_API_KEY` is; the bundled Claude Code plugin manifest sets it from user config. `CANON_ENVIRONMENT_ID` is still required with either variable.
|
|
124
|
+
|
|
125
|
+
`canon-setup` installs the bundled `/canon-register` and `/canon-configure` skills into `~/.claude/skills` and prints this MCP configuration.
|
|
126
|
+
|
|
127
|
+
Use `canon-claude` host mode when you want phone-controlled runtime sessions with setup and live controls.
|
|
92
128
|
|
|
93
129
|
## Development
|
|
94
130
|
|
package/dist/host.js
CHANGED
|
@@ -1482,13 +1482,7 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
1482
1482
|
const deliveredMessageIds = chunkedFailure
|
|
1483
1483
|
? chunkedFailure.deliveredMessageIds
|
|
1484
1484
|
: resume.deliveredMessageIds;
|
|
1485
|
-
const failure = classifyFinalDeliveryFailure(cause
|
|
1486
|
-
if (failure === 'already-delivered') {
|
|
1487
|
-
// The server's idempotency guard says this final is already durable.
|
|
1488
|
-
markFinalTurnDelivered(deliverTurn);
|
|
1489
|
-
console.error(`[canon-host] [${conversationId.slice(0, 8)}] Final reply was already delivered under this id`);
|
|
1490
|
-
return true;
|
|
1491
|
-
}
|
|
1485
|
+
const failure = classifyFinalDeliveryFailure(cause);
|
|
1492
1486
|
if (failure === 'permanent') {
|
|
1493
1487
|
// Re-sending is pointless — the same request fails the same way. Surface
|
|
1494
1488
|
// what can be surfaced and finish the turn instead of burning the retry
|
|
@@ -2089,6 +2083,7 @@ function createSession(conversationId, environment, agentId, client, typingSigna
|
|
|
2089
2083
|
if (!shouldApplyClaudeEchoedSessionState({
|
|
2090
2084
|
echoed: state,
|
|
2091
2085
|
dispatching: session.dispatchingInput !== null,
|
|
2086
|
+
active: session.activeInput !== null,
|
|
2092
2087
|
hasPendingFinalDelivery: Boolean(session.pendingFinalDelivery),
|
|
2093
2088
|
})) {
|
|
2094
2089
|
console.error(`[canon-host] [${conversationId.slice(0, 8)}] `
|
package/dist/session-state.d.ts
CHANGED
|
@@ -165,6 +165,8 @@ export declare function shouldApplyClaudeEchoedSessionState(input: {
|
|
|
165
165
|
echoed: ClaudeSessionRunState | undefined;
|
|
166
166
|
/** An input is mid-dispatch: reserved by the host, not yet with the SDK. */
|
|
167
167
|
dispatching: boolean;
|
|
168
|
+
/** The SDK has accepted a Canon input whose turn has not completed. */
|
|
169
|
+
active: boolean;
|
|
168
170
|
/** A final is still being handed to Canon; the turn is not over. */
|
|
169
171
|
hasPendingFinalDelivery: boolean;
|
|
170
172
|
}): boolean;
|
|
@@ -478,34 +480,19 @@ export declare function composeClaudeFinalText(input: {
|
|
|
478
480
|
streamedText?: string | null;
|
|
479
481
|
failureNotice?: string | null;
|
|
480
482
|
}): string | null;
|
|
481
|
-
export type ClaudeFinalDeliveryFailure = 'retry' | 'permanent'
|
|
483
|
+
export type ClaudeFinalDeliveryFailure = 'retry' | 'permanent';
|
|
482
484
|
/**
|
|
483
485
|
* How the host should react to a failed final-reply send.
|
|
484
486
|
*
|
|
485
487
|
* Core's `isRetryableCanonDeliveryError` is already right about what a retry
|
|
486
488
|
* can fix (429, 5xx, transport errors); what the host lacked was the other
|
|
487
489
|
* half — everything else is PERMANENT and must be surfaced instead of retried.
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
* a pure hash of agent + conversation + turnKey, an id can be taken by an
|
|
495
|
-
* ENTIRELY DIFFERENT answer to the same inbound message, written by a previous
|
|
496
|
-
* process. Reading that as "the final is delivered" would finalize the turn
|
|
497
|
-
* with the tail never sent, so it stays 'permanent': a stops-short notice beats
|
|
498
|
-
* silently dropping the end of an answer.
|
|
499
|
-
*
|
|
500
|
-
* Resume support does not soften this. Core absorbs the one conflict that IS
|
|
501
|
-
* provably ours — the first part of a resumed attempt, whose write may have
|
|
502
|
-
* landed before its response was lost — and reports every other one, so a
|
|
503
|
-
* conflict reaching this function is precisely the case that must not be read
|
|
504
|
-
* as success.
|
|
505
|
-
*/
|
|
506
|
-
export declare function classifyFinalDeliveryFailure(error: unknown, options?: {
|
|
507
|
-
chunked?: boolean;
|
|
508
|
-
}): ClaudeFinalDeliveryFailure;
|
|
490
|
+
* Exact message-id replays return success with `created: false`. A 409 from the
|
|
491
|
+
* server's idempotency guard therefore means the stored payload differs from
|
|
492
|
+
* this final and is permanent; treating it as delivered would retire a turn
|
|
493
|
+
* whose answer was never accepted.
|
|
494
|
+
*/
|
|
495
|
+
export declare function classifyFinalDeliveryFailure(error: unknown): ClaudeFinalDeliveryFailure;
|
|
509
496
|
/**
|
|
510
497
|
* The pending-delivery record for a final that has to be retried, carrying the
|
|
511
498
|
* progress the next attempt needs.
|
package/dist/session-state.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { USAGE_LIMIT_ERROR_PREFIXES } from '@anthropic-ai/claude-agent-sdk';
|
|
3
|
-
import { DEFAULT_CHUNKED_MESSAGE_TEXT_MAX_BYTES,
|
|
3
|
+
import { DEFAULT_CHUNKED_MESSAGE_TEXT_MAX_BYTES, isRetryableCanonDeliveryError, utf8ByteLength, VERB_LIMITS, } from '@canonmsg/core';
|
|
4
4
|
import { boundTrailBlockMap, buildTrailBlockId, buildUndeliverableFinalNotice as buildHostUndeliverableFinalNotice, normalizePlanStepStatus, normalizeTrailKey, PLAN_BLOCK_TITLE, renderPlanSteps, truncateFailureDetail, } from '@canonmsg/coding-agent-host';
|
|
5
5
|
export function createClaudeInputEnvelope(input) {
|
|
6
6
|
const sourceMessageId = input.sourceMessageId ?? null;
|
|
@@ -163,7 +163,7 @@ export function isClaudeTurnSlotReserved(state) {
|
|
|
163
163
|
export function shouldApplyClaudeEchoedSessionState(input) {
|
|
164
164
|
if (input.echoed !== 'idle')
|
|
165
165
|
return true;
|
|
166
|
-
return !input.dispatching && !input.hasPendingFinalDelivery;
|
|
166
|
+
return !input.dispatching && !input.active && !input.hasPendingFinalDelivery;
|
|
167
167
|
}
|
|
168
168
|
/** Claim the slot for an input about to be dispatched. Must precede any await. */
|
|
169
169
|
export function reserveClaudeTurnSlot(session) {
|
|
@@ -775,28 +775,12 @@ export function composeClaudeFinalText(input) {
|
|
|
775
775
|
* Core's `isRetryableCanonDeliveryError` is already right about what a retry
|
|
776
776
|
* can fix (429, 5xx, transport errors); what the host lacked was the other
|
|
777
777
|
* half — everything else is PERMANENT and must be surfaced instead of retried.
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
* sequential messages that abort on the first failure, so a 409 says only that
|
|
783
|
-
* ONE part id is taken, with nothing to say which — and since the part ids are
|
|
784
|
-
* a pure hash of agent + conversation + turnKey, an id can be taken by an
|
|
785
|
-
* ENTIRELY DIFFERENT answer to the same inbound message, written by a previous
|
|
786
|
-
* process. Reading that as "the final is delivered" would finalize the turn
|
|
787
|
-
* with the tail never sent, so it stays 'permanent': a stops-short notice beats
|
|
788
|
-
* silently dropping the end of an answer.
|
|
789
|
-
*
|
|
790
|
-
* Resume support does not soften this. Core absorbs the one conflict that IS
|
|
791
|
-
* provably ours — the first part of a resumed attempt, whose write may have
|
|
792
|
-
* landed before its response was lost — and reports every other one, so a
|
|
793
|
-
* conflict reaching this function is precisely the case that must not be read
|
|
794
|
-
* as success.
|
|
778
|
+
* Exact message-id replays return success with `created: false`. A 409 from the
|
|
779
|
+
* server's idempotency guard therefore means the stored payload differs from
|
|
780
|
+
* this final and is permanent; treating it as delivered would retire a turn
|
|
781
|
+
* whose answer was never accepted.
|
|
795
782
|
*/
|
|
796
|
-
export function classifyFinalDeliveryFailure(error
|
|
797
|
-
if (isCanonMessageIdConflict(error) && !options.chunked) {
|
|
798
|
-
return 'already-delivered';
|
|
799
|
-
}
|
|
783
|
+
export function classifyFinalDeliveryFailure(error) {
|
|
800
784
|
return isRetryableCanonDeliveryError(error) ? 'retry' : 'permanent';
|
|
801
785
|
}
|
|
802
786
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/claude-code-plugin",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"description": "Canon channel plugin for Claude Code — messaging where AI agents are first-class citizens",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/claude-agent-sdk": "0.3.220",
|
|
34
|
-
"@canonmsg/agent-sdk": "^7.1.
|
|
35
|
-
"@canonmsg/agent-tools": "^0.3.
|
|
36
|
-
"@canonmsg/coding-agent-host": "^0.3.
|
|
37
|
-
"@canonmsg/core": "^8.
|
|
38
|
-
"@canonmsg/rich-cards": "^0.8.
|
|
34
|
+
"@canonmsg/agent-sdk": "^7.1.3",
|
|
35
|
+
"@canonmsg/agent-tools": "^0.3.2",
|
|
36
|
+
"@canonmsg/coding-agent-host": "^0.3.1",
|
|
37
|
+
"@canonmsg/core": "^8.2.0",
|
|
38
|
+
"@canonmsg/rich-cards": "^0.8.6",
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
@@ -1,50 +1,59 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: canon-configure
|
|
3
|
-
description:
|
|
3
|
+
description: Use an existing Canon agent API key with Claude Code
|
|
4
4
|
user-invocable: true
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Write
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
# Use an existing Canon API key
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Point Claude Code at a Canon agent key the user already has.
|
|
13
|
+
|
|
14
|
+
## Hard rule
|
|
15
|
+
|
|
16
|
+
Never write or edit `~/.canon/agents.json`. A stored profile must carry the agent
|
|
17
|
+
identity **and** a complete environment-bound endpoint set (`agentId`,
|
|
18
|
+
`environmentId`, `baseUrl`, `streamUrl`, `rtdbUrl`, `firebaseApiKey`), and every
|
|
19
|
+
entry is validated in one pass on load — a single hand-written entry makes the
|
|
20
|
+
whole file unloadable for every Canon host on that machine. Only the register
|
|
21
|
+
CLI writes profiles.
|
|
13
22
|
|
|
14
23
|
## Steps
|
|
15
24
|
|
|
16
25
|
1. Ask the user for:
|
|
17
|
-
- **API key** —
|
|
18
|
-
- **
|
|
19
|
-
- **Agent name** (optional) — display name for reference
|
|
20
|
-
|
|
21
|
-
2. Validate the API key format — it must start with `agk_live_` and be non-empty.
|
|
26
|
+
- **API key** — starts with `agk_live_`
|
|
27
|
+
- **Canon environment** — `canon-prod-v1` for production, `canon-dev-v1` for dev
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
2. Give them the launch command for the key. Nothing is stored; the key lives in
|
|
30
|
+
the environment of the process they start:
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
"<profile>": {
|
|
29
|
-
"apiKey": "<key>",
|
|
30
|
-
"agentId": "",
|
|
31
|
-
"agentName": "<name or 'Unknown'>",
|
|
32
|
-
"registeredAt": "<current ISO date>"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
32
|
+
```bash
|
|
33
|
+
CANON_API_KEY=<key> CANON_ENVIRONMENT_ID=<environment> canon-claude --cwd /path/to/project
|
|
35
34
|
```
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
`CANON_ENVIRONMENT_ID` is required with a raw key — without it the host exits
|
|
37
|
+
before connecting. Sessions started this way are shown as
|
|
38
|
+
manual/non-revivable by `canon-necromance`, because no secret-bearing state is
|
|
39
|
+
persisted.
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
3. If the user wants a stored, revivable profile instead, run `/canon-register`.
|
|
42
|
+
Registration is the only path that writes `~/.canon/agents.json`; re-running it
|
|
43
|
+
with an existing `--profile` name refreshes that profile's credential in place.
|
|
44
|
+
|
|
45
|
+
4. Once a profile exists, it can be pinned for channel mode in `.mcp.json`:
|
|
40
46
|
|
|
41
|
-
If they have multiple agents and want to pin one, add to `.mcp.json`:
|
|
42
47
|
```json
|
|
43
48
|
{
|
|
44
49
|
"mcpServers": {
|
|
45
50
|
"canon-channel": {
|
|
51
|
+
"command": "canon-channel-server",
|
|
46
52
|
"env": { "CANON_AGENT": "<profile>" }
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
```
|
|
57
|
+
|
|
58
|
+
`CANON_AGENT` is required as soon as more than one profile exists — the host
|
|
59
|
+
refuses to guess between them.
|
package/skills/register/SKILL.md
CHANGED
|
@@ -18,7 +18,7 @@ Register a new Canon agent so it can send and receive messages. Each agent is sa
|
|
|
18
18
|
- **Agent name** — The display name for the agent in Canon
|
|
19
19
|
- **Description** — What the agent does (shown to users in Canon)
|
|
20
20
|
- **Owner phone number** — The Canon account owner's phone number in E.164 format (e.g., +15551234567)
|
|
21
|
-
- **Canon environment** — The trust-domain ID supplied by Canon (
|
|
21
|
+
- **Canon environment** — The trust-domain ID supplied by Canon (`canon-prod-v1` for production, `canon-dev-v1` for dev). There is no CLI default; the command fails without it.
|
|
22
22
|
- **Profile name** (optional) — A short identifier for this agent (e.g., "reviewer", "notifier"). Defaults to a sanitized version of the agent name.
|
|
23
23
|
|
|
24
24
|
2. Run the registration CLI:
|
|
@@ -26,18 +26,23 @@ Register a new Canon agent so it can send and receive messages. Each agent is sa
|
|
|
26
26
|
canon-register --environment "<environment>" --name "<name>" --description "<description>" --phone "<phone>" --profile "<profile>"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
`CANON_ENVIRONMENT_ID=<environment>` in the environment is equivalent to `--environment`.
|
|
30
|
+
|
|
29
31
|
3. Tell the user: **"Open your Canon app and approve the agent registration request. Waiting for approval..."**
|
|
30
32
|
|
|
31
33
|
The CLI will poll automatically for up to 5 minutes.
|
|
32
34
|
|
|
33
35
|
4. On approval, the CLI saves the agent to `~/.canon/agents.json` automatically.
|
|
34
36
|
|
|
35
|
-
5. Tell the user: **"Agent registered!
|
|
37
|
+
5. Tell the user: **"Agent registered! Start it with `CANON_AGENT=<profile> canon-claude`. Or specify a project: `CANON_AGENT=<profile> canon-claude --cwd /path/to/project`"**
|
|
38
|
+
|
|
39
|
+
`CANON_AGENT` is required once more than one profile exists — the host refuses to guess between them.
|
|
36
40
|
|
|
37
41
|
## Error handling
|
|
38
42
|
|
|
39
43
|
- If the registration fails, show the error and ask the user to retry.
|
|
40
44
|
- If status returns `"rejected"`, tell the user the registration was rejected by the owner.
|
|
41
45
|
- If polling times out after 5 minutes, tell the user to try again later.
|
|
42
|
-
- If the phone number format
|
|
46
|
+
- If the server returns `Invalid phone number format`, the number could not be parsed as a real number; ask the user to re-enter it in full E.164 form (`+<country code><number>`).
|
|
47
|
+
- If the server returns `Owner phone number not found on platform`, the number is valid but no Canon account uses it; ask the user for the phone number of their Canon account.
|
|
43
48
|
- If environment verification fails, do not retry against another URL. Confirm the environment ID and endpoint set with the user.
|