@clawling/clawchat-plugin-openclaw 2026.9.11-1 → 2026.9.13-2
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/dist/src/account-config-writes.js +67 -0
- package/dist/src/account-removal.js +44 -0
- package/dist/src/channel.js +30 -1
- package/dist/src/channel.setup.js +79 -42
- package/dist/src/commands.js +50 -27
- package/dist/src/config.js +83 -13
- package/dist/src/liveware-cli.js +49 -4
- package/dist/src/liveware-sample.js +16 -2
- package/dist/src/login.runtime.js +25 -29
- package/dist/src/outbound.js +4 -4
- package/dist/src/runtime.js +35 -32
- package/dist/src/tools.js +799 -685
- package/openclaw.plugin.json +150 -0
- package/package.json +1 -1
- package/src/account-config-writes.ts +74 -0
- package/src/account-removal.ts +52 -0
- package/src/channel.setup.ts +86 -43
- package/src/channel.ts +28 -1
- package/src/commands.ts +60 -26
- package/src/config.ts +113 -14
- package/src/liveware-cli.ts +52 -4
- package/src/liveware-sample.ts +24 -5
- package/src/login.runtime.ts +30 -26
- package/src/outbound.ts +4 -4
- package/src/runtime.ts +52 -36
- package/src/tools.ts +896 -768
|
@@ -11,6 +11,8 @@ import net from "node:net";
|
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import { DEFAULT_SKILLS_REF, OFFICIAL_SKILLS_BASE } from "./skill-update.js";
|
|
13
13
|
import { livewareCliEnv } from "./liveware-cli.js";
|
|
14
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
|
15
|
+
import { normalizeOpenclawClawlingAccountId } from "./config.js";
|
|
14
16
|
export const LIVEWARES_TARGET = "openclaw";
|
|
15
17
|
export const LIVEWARE_SAMPLE_ID = "liveware-sample";
|
|
16
18
|
export const LIVEWARE_SAMPLE_APP_NAME = "Liveware Sample";
|
|
@@ -127,6 +129,15 @@ async function localInstallMatches(appDir, entry) {
|
|
|
127
129
|
return false; // missing / unreadable — treat as "not installed"
|
|
128
130
|
}
|
|
129
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Suffix for the `app.tmp-…` / `app.old-…` staging dirs. The pid keeps two
|
|
134
|
+
* PROCESSES apart; the account id keeps two supervisors inside ONE process
|
|
135
|
+
* apart, which share a pid and could otherwise share a staging tree.
|
|
136
|
+
*/
|
|
137
|
+
function stagingSuffix(accountId) {
|
|
138
|
+
const id = normalizeOpenclawClawlingAccountId(accountId);
|
|
139
|
+
return id === DEFAULT_ACCOUNT_ID ? `${process.pid}` : `${process.pid}-${id}`;
|
|
140
|
+
}
|
|
130
141
|
/**
|
|
131
142
|
* Download + verify + atomically install the sample under `<sampleRoot>/app`.
|
|
132
143
|
* Preserves USER_DATA_FILES from an existing install. Throws on any failure;
|
|
@@ -155,7 +166,8 @@ export async function downloadLivewareSample(opts) {
|
|
|
155
166
|
await fs.promises.rm(path.join(opts.sampleRoot, name), { recursive: true, force: true }).catch(() => { });
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
|
-
const
|
|
169
|
+
const swapSuffix = stagingSuffix(opts.accountId);
|
|
170
|
+
const tmpDir = path.join(opts.sampleRoot, `app.tmp-${swapSuffix}`);
|
|
159
171
|
await fs.promises.rm(tmpDir, { recursive: true, force: true });
|
|
160
172
|
await fs.promises.mkdir(tmpDir, { recursive: true });
|
|
161
173
|
try {
|
|
@@ -182,7 +194,7 @@ export async function downloadLivewareSample(opts) {
|
|
|
182
194
|
// handle, and doing it first leaves no app/ at all when it fails. Renaming
|
|
183
195
|
// aside puts the new tree in place first and makes the delete best-effort:
|
|
184
196
|
// a leftover app.old-<pid> is swept on the next run, never a broken install.
|
|
185
|
-
const oldDir = path.join(opts.sampleRoot, `app.old-${
|
|
197
|
+
const oldDir = path.join(opts.sampleRoot, `app.old-${swapSuffix}`);
|
|
186
198
|
await fs.promises.rm(oldDir, { recursive: true, force: true }).catch(() => { });
|
|
187
199
|
if (fs.existsSync(appDir)) {
|
|
188
200
|
await withFsSwapRetry(() => fs.promises.rename(appDir, oldDir));
|
|
@@ -737,6 +749,7 @@ export class LivewareSampleSupervisor {
|
|
|
737
749
|
}
|
|
738
750
|
const { version, appDir } = await downloadLivewareSample({
|
|
739
751
|
fetchFn: deps.fetchFn, sampleRoot: deps.sampleRoot, ref: deps.ref,
|
|
752
|
+
accountId: deps.accountId,
|
|
740
753
|
});
|
|
741
754
|
if (this.bailIfStopped())
|
|
742
755
|
return;
|
|
@@ -826,6 +839,7 @@ export class LivewareSampleSupervisor {
|
|
|
826
839
|
try {
|
|
827
840
|
const dl = await downloadLivewareSample({
|
|
828
841
|
fetchFn: deps.fetchFn, sampleRoot: deps.sampleRoot, ref: deps.ref,
|
|
842
|
+
accountId: deps.accountId,
|
|
829
843
|
});
|
|
830
844
|
version = dl.version;
|
|
831
845
|
appDir = dl.appDir;
|
|
@@ -2,7 +2,9 @@ import { createInterface } from "node:readline/promises";
|
|
|
2
2
|
import { createOpenclawClawlingApiClient } from "./api-client.js";
|
|
3
3
|
import { ClawlingApiError } from "./api-types.js";
|
|
4
4
|
import { resolvePluginVersion } from "./plugin-report.js";
|
|
5
|
-
import {
|
|
5
|
+
import { openclawClawlingAccountSlotPath, patchOpenclawClawlingAccountSection } from "./account-config-writes.js";
|
|
6
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
|
7
|
+
import { CHANNEL_ID, mergeOpenclawClawchatRuntimePluginActivation, mergeOpenclawClawchatToolAllow, normalizeOpenclawClawlingAccountId, readOpenclawClawlingAccountSection, resolveOpenclawClawlingAccount, } from "./config.js";
|
|
6
8
|
import { getClawChatStore } from "./storage.js";
|
|
7
9
|
/**
|
|
8
10
|
* Platform tag sent to `/v1/agents/connect`. Identifies the host of this
|
|
@@ -24,8 +26,7 @@ export const AGENT_NOT_FOUND_CODE = 16001;
|
|
|
24
26
|
/**
|
|
25
27
|
* Thrown when redeeming an invite code would silently replace a live activation.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
* slot and `listOpenclawClawlingAccountIds()` returns a single account. Because
|
|
29
|
+
* Each account slot holds exactly one ClawChat identity. Because
|
|
29
30
|
* the stored `user_id` is replayed as a re-pair hint, redeeming a *new* invite
|
|
30
31
|
* code here cannot produce a second agent: the server binds the code to the
|
|
31
32
|
* existing agent, consumes it, creates no second agent and no second contact
|
|
@@ -45,9 +46,9 @@ export class ExistingActivationError extends Error {
|
|
|
45
46
|
" - RESTORE the identity above (re-pairs that same agent; if it was " +
|
|
46
47
|
"deleted this brings it back with its history): " +
|
|
47
48
|
"/clawchat-activate <CODE> --repair\n" +
|
|
48
|
-
" - To run a SECOND agent alongside this one instead,
|
|
49
|
-
"
|
|
50
|
-
"
|
|
49
|
+
" - To run a SECOND agent alongside this one instead, activate it as a " +
|
|
50
|
+
"named account: /clawchat-activate <CODE> --account <name> (or: openclaw " +
|
|
51
|
+
"channels add --channel clawchat-plugin-openclaw --account <name> --token <CODE>)");
|
|
51
52
|
this.name = "ExistingActivationError";
|
|
52
53
|
this.userId = userId;
|
|
53
54
|
this.agentId = agentId;
|
|
@@ -113,37 +114,30 @@ async function promptActivationIntentFromStdin(runtime, who) {
|
|
|
113
114
|
rl?.close();
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
|
-
function buildLoginConfig(cfg, result) {
|
|
117
|
-
const
|
|
118
|
-
const existing = (channels[CHANNEL_ID] ?? {});
|
|
117
|
+
function buildLoginConfig(cfg, accountId, result) {
|
|
118
|
+
const existing = readOpenclawClawlingAccountSection(cfg, accountId);
|
|
119
119
|
const groupMode = existing.groupMode === "mention" || existing.groupMode === "all"
|
|
120
120
|
? existing.groupMode
|
|
121
121
|
: "all";
|
|
122
122
|
const groupCommandMode = existing.groupCommandMode === "all" || existing.groupCommandMode === "off"
|
|
123
123
|
? existing.groupCommandMode
|
|
124
124
|
: "owner";
|
|
125
|
-
const
|
|
126
|
-
...existing,
|
|
125
|
+
const patch = {
|
|
127
126
|
enabled: true,
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
// Group policy defaults are root-level shared keys; named accounts inherit
|
|
128
|
+
// them and only carry credentials.
|
|
129
|
+
...(accountId === DEFAULT_ACCOUNT_ID ? { groupMode, groupCommandMode } : {}),
|
|
130
130
|
token: result.access_token,
|
|
131
|
+
// `undefined` deletes the key so a login without a refresh token never
|
|
132
|
+
// leaves a stale one behind.
|
|
133
|
+
refreshToken: result.refresh_token ? result.refresh_token : undefined,
|
|
131
134
|
...(result.agent.id ? { agentId: result.agent.id } : {}),
|
|
132
135
|
userId: result.agent.user_id,
|
|
133
136
|
ownerUserId: result.agent.owner_id,
|
|
134
137
|
};
|
|
135
|
-
|
|
136
|
-
nextSection.refreshToken = result.refresh_token;
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
delete nextSection.refreshToken;
|
|
140
|
-
}
|
|
141
|
-
return mergeOpenclawClawchatRuntimePluginActivation(mergeOpenclawClawchatToolAllow({
|
|
142
|
-
...cfg,
|
|
143
|
-
channels: { ...channels, [CHANNEL_ID]: nextSection },
|
|
144
|
-
}));
|
|
138
|
+
return mergeOpenclawClawchatRuntimePluginActivation(mergeOpenclawClawchatToolAllow(patchOpenclawClawlingAccountSection(cfg, accountId, patch)));
|
|
145
139
|
}
|
|
146
|
-
async function persistLoginConfig(params, result) {
|
|
140
|
+
async function persistLoginConfig(accountId, params, result) {
|
|
147
141
|
if (params.mutateConfigFile) {
|
|
148
142
|
params.runtime.log(`Persisting ClawChat credentials and plugin activation for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id} with Gateway restart intent.`);
|
|
149
143
|
await params.mutateConfigFile({
|
|
@@ -152,7 +146,7 @@ async function persistLoginConfig(params, result) {
|
|
|
152
146
|
reason: "clawchat-plugin-openclaw credentials changed",
|
|
153
147
|
},
|
|
154
148
|
mutate(draft) {
|
|
155
|
-
Object.assign(draft, buildLoginConfig(draft, result));
|
|
149
|
+
Object.assign(draft, buildLoginConfig(draft, accountId, result));
|
|
156
150
|
},
|
|
157
151
|
});
|
|
158
152
|
params.runtime.log(`ClawChat credentials and plugin activation persisted for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id}.`);
|
|
@@ -160,7 +154,7 @@ async function persistLoginConfig(params, result) {
|
|
|
160
154
|
}
|
|
161
155
|
if (params.persistConfig) {
|
|
162
156
|
params.runtime.log(`Persisting ClawChat credentials and plugin activation for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id}.`);
|
|
163
|
-
await params.persistConfig(buildLoginConfig(params.cfg, result));
|
|
157
|
+
await params.persistConfig(buildLoginConfig(params.cfg, accountId, result));
|
|
164
158
|
params.runtime.log(`ClawChat credentials and plugin activation persisted for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id}.`);
|
|
165
159
|
return;
|
|
166
160
|
}
|
|
@@ -198,10 +192,11 @@ function readOptionalConnectString(value, fieldName) {
|
|
|
198
192
|
*/
|
|
199
193
|
export async function runOpenclawClawlingLogin(params) {
|
|
200
194
|
const { cfg, runtime } = params;
|
|
195
|
+
const accountId = normalizeOpenclawClawlingAccountId(params.accountId);
|
|
201
196
|
// `resolveOpenclawClawlingAccount` falls back to the built-in
|
|
202
197
|
// `DEFAULT_BASE_URL` / `DEFAULT_WEBSOCKET_URL` when the operator has not
|
|
203
198
|
// overridden them, so login works without a prior `openclaw channels setup --channel clawchat-plugin-openclaw`.
|
|
204
|
-
const account = resolveOpenclawClawlingAccount(cfg);
|
|
199
|
+
const account = resolveOpenclawClawlingAccount(cfg, accountId);
|
|
205
200
|
// Fork before the invite code is even read, so neither branch spends the code
|
|
206
201
|
// before the outcome is settled. "Live" is decided by whether a usable token
|
|
207
202
|
// remains: auto-logout (§C) blanks the tokens but preserves the identity, so a
|
|
@@ -315,8 +310,9 @@ export async function runOpenclawClawlingLogin(params) {
|
|
|
315
310
|
}
|
|
316
311
|
: {}),
|
|
317
312
|
};
|
|
318
|
-
|
|
319
|
-
|
|
313
|
+
const configPath = openclawClawlingAccountSlotPath(params.cfg, accountId);
|
|
314
|
+
runtime.log(`Updating config: ${configPath}.token=[REDACTED] agentId=${normalizedResult.agent.id || "-"} userId=${normalizedResult.agent.user_id} ownerUserId=${normalizedResult.agent.owner_id}${normalizedResult.refresh_token ? " refreshToken=[REDACTED]" : ""} plugins.entries.${CHANNEL_ID}.enabled=true plugins.allow+=${CHANNEL_ID} …`);
|
|
315
|
+
await persistLoginConfig(accountId, params, normalizedResult);
|
|
320
316
|
try {
|
|
321
317
|
const store = params.store ??
|
|
322
318
|
getClawChatStore({
|
package/dist/src/outbound.js
CHANGED
|
@@ -673,8 +673,8 @@ export const openclawClawlingOutbound = {
|
|
|
673
673
|
textChunkLimit: 4000,
|
|
674
674
|
...createAttachedChannelResultAdapter({
|
|
675
675
|
channel: CHANNEL_ID,
|
|
676
|
-
sendText: async ({ cfg, to, text }) => {
|
|
677
|
-
const account = resolveOpenclawClawlingAccount(cfg);
|
|
676
|
+
sendText: async ({ cfg, to, text, accountId }) => {
|
|
677
|
+
const account = resolveOpenclawClawlingAccount(cfg, accountId);
|
|
678
678
|
const client = getOpenclawClawlingClient(account.accountId) ??
|
|
679
679
|
(await waitForOpenclawClawlingClient(account.accountId));
|
|
680
680
|
const target = parseOpenclawRecipient(to);
|
|
@@ -709,8 +709,8 @@ export const openclawClawlingOutbound = {
|
|
|
709
709
|
messageId: result?.messageId ?? messageId,
|
|
710
710
|
};
|
|
711
711
|
},
|
|
712
|
-
sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile }) => {
|
|
713
|
-
const account = resolveOpenclawClawlingAccount(cfg);
|
|
712
|
+
sendMedia: async ({ cfg, to, text, mediaUrl, mediaAccess, mediaLocalRoots, mediaReadFile, accountId }) => {
|
|
713
|
+
const account = resolveOpenclawClawlingAccount(cfg, accountId);
|
|
714
714
|
const client = getOpenclawClawlingClient(account.accountId) ??
|
|
715
715
|
(await waitForOpenclawClawlingClient(account.accountId));
|
|
716
716
|
if (!mediaUrl?.trim()) {
|
package/dist/src/runtime.js
CHANGED
|
@@ -6,13 +6,14 @@ import { createOpenclawClawlingClient, resolveOpenclawClawlingDeviceId } from ".
|
|
|
6
6
|
import { createOpenclawClawlingApiClient } from "./api-client.js";
|
|
7
7
|
import { buildActivationBootstrapText } from "./activation-greeting.js";
|
|
8
8
|
import { reportPluginVersionSafe, resolvePluginVersion } from "./plugin-report.js";
|
|
9
|
-
import
|
|
10
|
-
import { ensureLivewareCli, livewareCliHomeDir, resolveLivewarePath } from "./liveware-cli.js";
|
|
9
|
+
import { ensureLivewareCli, livewareCliHomeDir, livewareSampleRootDir, resolveLivewarePath, } from "./liveware-cli.js";
|
|
11
10
|
import { LivewareSampleSupervisor, } from "./liveware-sample.js";
|
|
12
11
|
import { ClawlingApiError } from "./api-types.js";
|
|
13
12
|
import { RefreshManager } from "./refresh-manager.js";
|
|
14
13
|
import { runOpenclawClawlingLogin, } from "./login.runtime.js";
|
|
15
|
-
import { CHANNEL_ID, effectiveOutputVisibility, effectiveGroupCommandMode, effectiveGroupMode, hasOpenclawClawlingConnectCredentials, resolveOpenclawClawlingAccount, CLAWCHAT_REFRESH_TOKEN_ENV, } from "./config.js";
|
|
14
|
+
import { CHANNEL_ID, effectiveOutputVisibility, effectiveGroupCommandMode, effectiveGroupMode, hasOpenclawClawlingConnectCredentials, normalizeOpenclawClawlingAccountId, readOpenclawClawlingAccountSection, resolveOpenclawClawlingAccount, CLAWCHAT_REFRESH_TOKEN_ENV, } from "./config.js";
|
|
15
|
+
import { patchOpenclawClawlingAccountSection } from "./account-config-writes.js";
|
|
16
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
|
16
17
|
import { isValidChatId } from "./ws-client.js";
|
|
17
18
|
import { dispatchOpenclawClawlingInbound } from "./inbound.js";
|
|
18
19
|
import { PendingConsentStore, cleanupTombstonedManagedSkills, handleOwnerConsentReply, managedSkillExists, readLocalSkillState, resolveBundledSkillsDir, resolveManagedSkillsDir, runSkillUpdateCheck, } from "./skill-update.js";
|
|
@@ -130,22 +131,22 @@ export function classifyHelloFailReason(reason) {
|
|
|
130
131
|
return "token-rejected";
|
|
131
132
|
return "generic";
|
|
132
133
|
}
|
|
133
|
-
/** Read `channels.<CHANNEL_ID>.refreshToken` from a live config, or null. */
|
|
134
134
|
/**
|
|
135
|
-
* The refresh token paired with the
|
|
136
|
-
* `refreshToken` wins. `CLAWCHAT_REFRESH_TOKEN`
|
|
137
|
-
*
|
|
138
|
-
* of its own
|
|
139
|
-
*
|
|
140
|
-
* never paired with a stray refresh token left in the environment.
|
|
135
|
+
* The refresh token paired with the account's access token. The account
|
|
136
|
+
* section's `refreshToken` wins. `CLAWCHAT_REFRESH_TOKEN` is process-wide, so
|
|
137
|
+
* it is honoured ONLY for the default account and ONLY when that section
|
|
138
|
+
* carries no `token` of its own (i.e. the access token itself came from
|
|
139
|
+
* `CLAWCHAT_TOKEN`) — an env-provisioned pair can rotate, while a login-written
|
|
140
|
+
* token is never paired with a stray refresh token left in the environment.
|
|
141
141
|
*/
|
|
142
|
-
function readConfigRefreshToken(cfg, env = process.env) {
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const refreshToken = isRecord(channel) ? channel.refreshToken : undefined;
|
|
142
|
+
function readConfigRefreshToken(cfg, accountId, env = process.env) {
|
|
143
|
+
const section = readOpenclawClawlingAccountSection(cfg, accountId);
|
|
144
|
+
const refreshToken = section.refreshToken;
|
|
146
145
|
if (typeof refreshToken === "string" && refreshToken.trim())
|
|
147
146
|
return refreshToken.trim();
|
|
148
|
-
|
|
147
|
+
if (normalizeOpenclawClawlingAccountId(accountId) !== DEFAULT_ACCOUNT_ID)
|
|
148
|
+
return null;
|
|
149
|
+
const configToken = section.token;
|
|
149
150
|
if (typeof configToken === "string" && configToken.trim())
|
|
150
151
|
return null;
|
|
151
152
|
const envRefreshToken = env[CLAWCHAT_REFRESH_TOKEN_ENV];
|
|
@@ -457,6 +458,12 @@ export async function maybeAutoActivateFromEnv(params) {
|
|
|
457
458
|
const code = (params.connectCode ?? "").trim();
|
|
458
459
|
if (!code)
|
|
459
460
|
return params.account;
|
|
461
|
+
// The env var is process-wide: it can only ever describe ONE identity, and
|
|
462
|
+
// that is the default account. A named account never redeems it.
|
|
463
|
+
if (normalizeOpenclawClawlingAccountId(params.account.accountId) !== DEFAULT_ACCOUNT_ID) {
|
|
464
|
+
params.log?.info?.(`[${params.account.accountId}] clawchat-plugin-openclaw ${CONNECT_CODE_ENV} present but ignored: env activation is for the default account only`);
|
|
465
|
+
return params.account;
|
|
466
|
+
}
|
|
460
467
|
if (accountHasConnectCredentials(params.account))
|
|
461
468
|
return params.account;
|
|
462
469
|
const accountId = params.account.accountId;
|
|
@@ -489,7 +496,7 @@ export async function maybeAutoActivateFromEnv(params) {
|
|
|
489
496
|
params.log?.error?.(`[${accountId}] clawchat-plugin-openclaw ${CONNECT_CODE_ENV} activation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
490
497
|
return params.account;
|
|
491
498
|
}
|
|
492
|
-
const next = resolveAccount(params.cfg);
|
|
499
|
+
const next = resolveAccount(params.cfg, accountId);
|
|
493
500
|
params.log?.info?.(`[${accountId}] clawchat-plugin-openclaw ${CONNECT_CODE_ENV} activation succeeded hasToken=${Boolean(next.token)}`);
|
|
494
501
|
return next;
|
|
495
502
|
}
|
|
@@ -769,7 +776,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
769
776
|
userId: account.userId,
|
|
770
777
|
ownerUserId: account.ownerUserId,
|
|
771
778
|
accessToken: account.token,
|
|
772
|
-
refreshToken: readConfigRefreshToken(cfg),
|
|
779
|
+
refreshToken: readConfigRefreshToken(cfg, accountId),
|
|
773
780
|
loginMethod: "config-seed",
|
|
774
781
|
deviceId: CHANNEL_ID,
|
|
775
782
|
});
|
|
@@ -851,13 +858,16 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
851
858
|
// `RuntimeConnectionStore` only `Pick`s a subset of its methods, so widen
|
|
852
859
|
// through the structural `LivewareSampleStoreLike` contract (Task 8/4).
|
|
853
860
|
store: store,
|
|
854
|
-
|
|
861
|
+
// Per account: two supervisors in one process otherwise install, swap and
|
|
862
|
+
// delete inside the SAME `app/` tree. `default` keeps the legacy path.
|
|
863
|
+
sampleRoot: livewareSampleRootDir(stateDirForSample ?? "", accountId),
|
|
855
864
|
resolveToken: () => account.token ?? "",
|
|
856
865
|
resolveAgentUserId: () => account.userId ?? null,
|
|
857
866
|
resolveLivewarePath: () => resolveLivewarePath(stateDirForSample),
|
|
858
|
-
// Per-instance HOME for the liveware CLI: its credentials
|
|
859
|
-
// in the host-global `$HOME/.clawling`, which a second
|
|
860
|
-
|
|
867
|
+
// Per-instance AND per-account HOME for the liveware CLI: its credentials
|
|
868
|
+
// otherwise live in the host-global `$HOME/.clawling`, which a second
|
|
869
|
+
// profile — or a second account in this very process — clobbers.
|
|
870
|
+
cliHome: livewareCliHomeDir(stateDirForSample, accountId),
|
|
861
871
|
cliReady: livewareCliReady,
|
|
862
872
|
listApps: async () => {
|
|
863
873
|
const client = getConversationApiClient();
|
|
@@ -906,7 +916,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
906
916
|
? runtimeConfig.mutateConfigFile
|
|
907
917
|
: undefined;
|
|
908
918
|
};
|
|
909
|
-
// §0/§C.1 — write the
|
|
919
|
+
// §0/§C.1 — write the account slot's `token`/`refreshToken` keys. `tokens=null`
|
|
910
920
|
// blanks them (auto-logout); otherwise persists the rotated pair.
|
|
911
921
|
const persistConfigTokens = async (tokens) => {
|
|
912
922
|
const mutateConfigFile = resolveMutateConfigFile();
|
|
@@ -917,17 +927,10 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
917
927
|
await mutateConfigFile({
|
|
918
928
|
afterWrite: { mode: "none", reason: "clawchat-plugin-openclaw token refresh" },
|
|
919
929
|
mutate(draft) {
|
|
920
|
-
|
|
921
|
-
const existing = (channels[CHANNEL_ID] ?? {});
|
|
922
|
-
const nextSection = {
|
|
923
|
-
...existing,
|
|
930
|
+
Object.assign(draft, patchOpenclawClawlingAccountSection(draft, accountId, {
|
|
924
931
|
token: tokens ? tokens.accessToken : "",
|
|
925
932
|
refreshToken: tokens ? tokens.refreshToken : "",
|
|
926
|
-
};
|
|
927
|
-
Object.assign(draft, {
|
|
928
|
-
...draft,
|
|
929
|
-
channels: { ...channels, [CHANNEL_ID]: nextSection },
|
|
930
|
-
});
|
|
933
|
+
}));
|
|
931
934
|
},
|
|
932
935
|
});
|
|
933
936
|
};
|
|
@@ -1016,7 +1019,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1016
1019
|
(activationAccount.source === "sqlite" && store?.getActivationCredentials
|
|
1017
1020
|
? store.getActivationCredentials({ platform: "openclaw", accountId })?.refreshToken
|
|
1018
1021
|
: null) ??
|
|
1019
|
-
readConfigRefreshToken(cfg);
|
|
1022
|
+
readConfigRefreshToken(cfg, accountId);
|
|
1020
1023
|
const refreshManager = new RefreshManager({
|
|
1021
1024
|
baseUrl: account.baseUrl,
|
|
1022
1025
|
deviceId: refreshDeviceId,
|