@parall/parall 1.33.0 → 1.35.0
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/gateway.d.ts +9 -0
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +35 -2
- package/dist/index.bundle.mjs +107 -5
- package/package.json +3 -3
- package/skills/parall-wiki/SKILL.md +88 -89
- package/src/gateway.ts +39 -2
package/dist/gateway.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import type { PluginRuntime } from 'openclaw/plugin-sdk';
|
|
1
2
|
import type { ChannelPlugin } from 'openclaw/plugin-sdk/core';
|
|
2
3
|
/** Extract ChannelGatewayAdapter from ChannelPlugin (removed from public SDK exports in 2026.3.24). */
|
|
3
4
|
type ChannelGatewayAdapter<T = unknown> = NonNullable<ChannelPlugin<T>['gateway']>;
|
|
5
|
+
import { type DispatchAdapter } from '@parall/agent-core';
|
|
4
6
|
import type { ResolvedParallAccount } from './types.js';
|
|
7
|
+
export declare function createOpenClawDispatchAdapter(opts: {
|
|
8
|
+
core: PluginRuntime;
|
|
9
|
+
cfg: Record<string, unknown>;
|
|
10
|
+
accountId: string;
|
|
11
|
+
sessionsDir: string;
|
|
12
|
+
workspaceDir: string;
|
|
13
|
+
}): DispatchAdapter;
|
|
5
14
|
export declare const parallGateway: ChannelGatewayAdapter<ResolvedParallAccount>;
|
|
6
15
|
export {};
|
|
7
16
|
//# sourceMappingURL=gateway.d.ts.map
|
package/dist/gateway.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,uGAAuG;AACvG,KAAK,qBAAqB,CAAC,CAAC,GAAG,OAAO,IAAI,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AACnF,OAAO,EAOL,KAAK,eAAe,EAGrB,MAAM,oBAAoB,CAAC;AAqB5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAiKxD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,aAAa,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,eAAe,CA0KlB;AAED,eAAO,MAAM,aAAa,EAAE,qBAAqB,CAAC,qBAAqB,CAmJtE,CAAC"}
|
package/dist/gateway.js
CHANGED
|
@@ -145,8 +145,41 @@ async function waitForOpenClawSessionId(sessionsDir, sessionKey, timeoutMs = 10_
|
|
|
145
145
|
}
|
|
146
146
|
return null;
|
|
147
147
|
}
|
|
148
|
-
function createOpenClawDispatchAdapter(opts) {
|
|
148
|
+
export function createOpenClawDispatchAdapter(opts) {
|
|
149
149
|
const activeStreams = new Map();
|
|
150
|
+
let configFallbackWarned = false;
|
|
151
|
+
/**
|
|
152
|
+
* Resolve the config for one dispatch. OpenClaw hot-reloads
|
|
153
|
+
* `agents.defaults.model` / `models.providers` WITHOUT restarting channel
|
|
154
|
+
* plugins, so the `ctx.cfg` snapshot captured at startAccount goes stale the
|
|
155
|
+
* moment the operator switches the agent's model — every run would keep the
|
|
156
|
+
* old model until the whole gateway restarts. `core.config.current()` is the
|
|
157
|
+
* host's live (hot-reload-refreshed) config; fall back to the startup
|
|
158
|
+
* snapshot on hosts that predate the API. The fallback warns once per
|
|
159
|
+
* degradation streak (latch resets on success) — silently dispatching with
|
|
160
|
+
* the stale snapshot would recreate the invisible model-switch mismatch this
|
|
161
|
+
* function exists to fix.
|
|
162
|
+
*/
|
|
163
|
+
function resolveDispatchConfig(log) {
|
|
164
|
+
try {
|
|
165
|
+
const current = opts.core.config?.current?.();
|
|
166
|
+
if (current && typeof current === 'object') {
|
|
167
|
+
configFallbackWarned = false;
|
|
168
|
+
return current;
|
|
169
|
+
}
|
|
170
|
+
if (!configFallbackWarned) {
|
|
171
|
+
configFallbackWarned = true;
|
|
172
|
+
log?.warn?.(`parall[${opts.accountId}]: config.current() unavailable on this host — dispatching with the startup config snapshot; model switches need a gateway restart until then`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
if (!configFallbackWarned) {
|
|
177
|
+
configFallbackWarned = true;
|
|
178
|
+
log?.warn?.(`parall[${opts.accountId}]: config.current() failed (${String(err)}) — dispatching with the startup config snapshot; model switches need a gateway restart until then`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return opts.cfg;
|
|
182
|
+
}
|
|
150
183
|
return {
|
|
151
184
|
abortDispatch(sessionKey) {
|
|
152
185
|
activeStreams.get(sessionKey)?.fail(new Error('dispatch deadline exceeded'));
|
|
@@ -172,7 +205,7 @@ function createOpenClawDispatchAdapter(opts) {
|
|
|
172
205
|
let turnGroupKey = '';
|
|
173
206
|
const run = opts.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
174
207
|
ctx: buildInboundCtx(opts.core, opts.accountId, event, sessionKey, promptBody, earlierEvents),
|
|
175
|
-
cfg:
|
|
208
|
+
cfg: resolveDispatchConfig(context.log),
|
|
176
209
|
dispatcherOptions: {
|
|
177
210
|
deliver: async (payload) => {
|
|
178
211
|
const replyText = payload.text?.trim();
|
package/dist/index.bundle.mjs
CHANGED
|
@@ -27010,6 +27010,7 @@ var ENDPOINTS = {
|
|
|
27010
27010
|
// Org-scoped
|
|
27011
27011
|
ORG: (orgId) => `${API_BASE}/orgs/${orgId}`,
|
|
27012
27012
|
ORG_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/members`,
|
|
27013
|
+
TEAMS: (orgId) => `${API_BASE}/orgs/${orgId}/teams`,
|
|
27013
27014
|
ORG_MEMBERS_ONLINE: (orgId) => `${API_BASE}/orgs/${orgId}/members/online`,
|
|
27014
27015
|
ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
|
|
27015
27016
|
ORG_MEMBER_CHATS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/chats`,
|
|
@@ -27054,6 +27055,7 @@ var ENDPOINTS = {
|
|
|
27054
27055
|
AGENT: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}`,
|
|
27055
27056
|
AGENT_API_KEYS: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/api-keys`,
|
|
27056
27057
|
AGENT_API_KEY: (orgId, agentId, key) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/api-keys/${key}`,
|
|
27058
|
+
AGENT_API_KEY_REGENERATE: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/api-keys/regenerate`,
|
|
27057
27059
|
AGENT_AVATAR: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/avatar`,
|
|
27058
27060
|
AGENT_ACTIVITY: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/activity`,
|
|
27059
27061
|
AGENT_MONITOR: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/monitor`,
|
|
@@ -27093,6 +27095,7 @@ var ENDPOINTS = {
|
|
|
27093
27095
|
MACHINE_AGENT_WORKSPACE_SETUP: (orgId, machineId, agentId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/agents/${agentId}/workspace/setup`,
|
|
27094
27096
|
MACHINE_LLM_SOURCE: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/llm-source`,
|
|
27095
27097
|
MACHINE_PROVIDER_ENABLED: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/provider-enabled`,
|
|
27098
|
+
MACHINE_CAPABILITIES: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/capabilities`,
|
|
27096
27099
|
MACHINE_RUNTIME_AUTH: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/runtime-auth`,
|
|
27097
27100
|
MACHINE_KEYS: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/keys`,
|
|
27098
27101
|
MACHINE_KEY: (orgId, machineId, keyId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/keys/${keyId}`,
|
|
@@ -27113,6 +27116,12 @@ var ENDPOINTS = {
|
|
|
27113
27116
|
MACHINES_ME_AGENT_WORKSPACE_STATE: (agentId) => `${API_BASE}/machines/me/agents/${agentId}/workspace-state`,
|
|
27114
27117
|
MACHINES_ME_WS_TICKET: `${API_BASE}/machines/me/ws/ticket`,
|
|
27115
27118
|
MACHINES_ME_BROWSE_RESPONSE: (requestId) => `${API_BASE}/machines/me/browse-response/${requestId}`,
|
|
27119
|
+
// Hosted browser live-viewer control plane (api-server, NOT clip-service):
|
|
27120
|
+
// the web client drives WebRTC signaling + tab nav through VIEWER_COMMAND;
|
|
27121
|
+
// api-server brokers each command to the host daemon via the machine:{id}
|
|
27122
|
+
// request/reply bridge (mirrors filesystem browse), and the daemon replies on
|
|
27123
|
+
// VIEWER_RESPONSE. See docs/engineering-design/hosted-browser-provider-design.md.
|
|
27124
|
+
MACHINES_ME_BROWSER_PROFILE_VIEWER_RESPONSE: (requestId) => `${API_BASE}/machines/me/browser-profiles/viewer-response/${requestId}`,
|
|
27116
27125
|
// Tasks (org-scoped)
|
|
27117
27126
|
TASKS: (orgId) => `${API_BASE}/orgs/${orgId}/tasks`,
|
|
27118
27127
|
TASK: (orgId, taskId) => `${API_BASE}/orgs/${orgId}/tasks/${taskId}`,
|
|
@@ -27178,6 +27187,9 @@ var ENDPOINTS = {
|
|
|
27178
27187
|
// Wiki Path Scopes (AFCS ACL)
|
|
27179
27188
|
WIKI_PATH_SCOPES: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/path-scopes`,
|
|
27180
27189
|
WIKI_PATH_SCOPE: (orgId, wikiId, scopeId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/path-scopes/${scopeId}`,
|
|
27190
|
+
// Wiki Path Restrictions (AFCS narrowing ACL — private subtrees)
|
|
27191
|
+
WIKI_RESTRICTIONS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/restrictions`,
|
|
27192
|
+
WIKI_RESTRICTION: (orgId, wikiId, restrictionId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/restrictions/${restrictionId}`,
|
|
27181
27193
|
WIKI_ACCESS_STATUS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/access-status`,
|
|
27182
27194
|
WIKI_ACCESS_REQUESTS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/access-requests`,
|
|
27183
27195
|
// Wiki History (commits, file commits, blame)
|
|
@@ -27226,6 +27238,8 @@ var ENDPOINTS = {
|
|
|
27226
27238
|
PUSH_VAPID_KEY: `${API_BASE}/push/vapid-key`,
|
|
27227
27239
|
// Notification preferences
|
|
27228
27240
|
NOTIFICATION_PREFERENCES: `${API_BASE}/notification-preferences`,
|
|
27241
|
+
// Unified search (messages + tasks + wiki, org-scoped)
|
|
27242
|
+
SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/search`,
|
|
27229
27243
|
// Feature flags (org-scoped, server-evaluated)
|
|
27230
27244
|
FEATURE_FLAGS: (orgId) => `${API_BASE}/orgs/${orgId}/feature-flags`,
|
|
27231
27245
|
// Billing & Credits (org-scoped)
|
|
@@ -27241,6 +27255,7 @@ var ENDPOINTS = {
|
|
|
27241
27255
|
// Clips (org-scoped, served by clip-service)
|
|
27242
27256
|
CLIPS: (orgId) => `${CLIP_BASE}/orgs/${orgId}/clips`,
|
|
27243
27257
|
CLIP: (orgId, clipId) => `${CLIP_BASE}/orgs/${orgId}/clips/${clipId}`,
|
|
27258
|
+
CLIPS_BULK_METADATA: (orgId) => `${CLIP_BASE}/orgs/${orgId}/clips/bulk-metadata`,
|
|
27244
27259
|
CLIP_AGENTS: (orgId, clipId) => `${CLIP_BASE}/orgs/${orgId}/clips/${clipId}/agents`,
|
|
27245
27260
|
AGENT_CLIPS: (orgId, agentId) => `${CLIP_BASE}/orgs/${orgId}/agents/${agentId}/clips`,
|
|
27246
27261
|
AGENT_CLIP: (orgId, agentId, clipId) => `${CLIP_BASE}/orgs/${orgId}/agents/${agentId}/clips/${clipId}`,
|
|
@@ -27253,6 +27268,9 @@ var ENDPOINTS = {
|
|
|
27253
27268
|
BROWSER_PROFILE_RESET: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/reset`,
|
|
27254
27269
|
BROWSER_PROFILE_CONSENTS: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/consents`,
|
|
27255
27270
|
BROWSER_PROFILE_CONSENT: (orgId, profileId, clipId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/consents/${clipId}`,
|
|
27271
|
+
// Live viewer command — on api-server (API_BASE), not clip-service: it rides
|
|
27272
|
+
// the machine control-plane request/reply bridge that lives in api-server.
|
|
27273
|
+
BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
|
|
27256
27274
|
// Clip registry (global, served by clip-service → Pinix Hub proxy)
|
|
27257
27275
|
CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`
|
|
27258
27276
|
};
|
|
@@ -27329,6 +27347,7 @@ var WS_EVENTS = {
|
|
|
27329
27347
|
MACHINE_CONFIG_UPDATED: "machine.config.updated",
|
|
27330
27348
|
MACHINE_CLIP_SYNC: "machine.clip.sync",
|
|
27331
27349
|
MACHINE_BROWSER_PROFILE_LIFECYCLE: "machine.browser_profile.lifecycle",
|
|
27350
|
+
MACHINE_BROWSER_PROFILE_VIEWER: "machine.browser_profile.viewer",
|
|
27332
27351
|
AGENT_NEW_SESSION: "agent.new_session",
|
|
27333
27352
|
CLIP_CREATED: "clip.created",
|
|
27334
27353
|
CLIP_REMOVED: "clip.removed",
|
|
@@ -27359,8 +27378,13 @@ var ParallClient = class _ParallClient {
|
|
|
27359
27378
|
/** Proactive refresh when token expires within this window (seconds). */
|
|
27360
27379
|
static REFRESH_THRESHOLD_S = 5 * 60;
|
|
27361
27380
|
static normalizeFetchError(err) {
|
|
27362
|
-
if (typeof DOMException !== "undefined" && err instanceof DOMException
|
|
27363
|
-
|
|
27381
|
+
if (typeof DOMException !== "undefined" && err instanceof DOMException) {
|
|
27382
|
+
if (err.name === "TimeoutError") {
|
|
27383
|
+
return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
|
|
27384
|
+
}
|
|
27385
|
+
if (err.name === "AbortError") {
|
|
27386
|
+
return new ApiError(0, "Request aborted", "REQUEST_ABORTED");
|
|
27387
|
+
}
|
|
27364
27388
|
}
|
|
27365
27389
|
const apiError = new ApiError(0, "Network request failed", "NETWORK_ERROR");
|
|
27366
27390
|
if (err instanceof Error && err.message && err.message !== "Failed to fetch") {
|
|
@@ -27467,13 +27491,18 @@ var ParallClient = class _ParallClient {
|
|
|
27467
27491
|
url += `?${qs}`;
|
|
27468
27492
|
}
|
|
27469
27493
|
const headers = this.buildHeaders();
|
|
27494
|
+
const timeoutSignal = AbortSignal.timeout(opts?.timeoutMs ?? 15e3);
|
|
27495
|
+
const signal = opts?.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
|
|
27470
27496
|
let res;
|
|
27471
27497
|
try {
|
|
27472
27498
|
res = await fetch(url, {
|
|
27473
27499
|
method,
|
|
27474
27500
|
headers,
|
|
27475
27501
|
body: body ? JSON.stringify(body) : void 0,
|
|
27476
|
-
signal
|
|
27502
|
+
signal,
|
|
27503
|
+
// keepalive lets a request fired during page unload (e.g. the browser
|
|
27504
|
+
// viewer's stream.close on pagehide) outlive the document.
|
|
27505
|
+
keepalive: opts?.keepalive
|
|
27477
27506
|
});
|
|
27478
27507
|
} catch (err) {
|
|
27479
27508
|
throw _ParallClient.normalizeFetchError(err);
|
|
@@ -27644,6 +27673,10 @@ var ParallClient = class _ParallClient {
|
|
|
27644
27673
|
const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS(orgId));
|
|
27645
27674
|
return res.data;
|
|
27646
27675
|
}
|
|
27676
|
+
async getTeams(orgId) {
|
|
27677
|
+
const res = await this.request("GET", ENDPOINTS.TEAMS(orgId));
|
|
27678
|
+
return res.data;
|
|
27679
|
+
}
|
|
27647
27680
|
async getOnlineMembers(orgId) {
|
|
27648
27681
|
const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS_ONLINE(orgId));
|
|
27649
27682
|
return res.user_ids ?? [];
|
|
@@ -27882,6 +27915,10 @@ var ParallClient = class _ParallClient {
|
|
|
27882
27915
|
async createAgentApiKey(orgId, agentId) {
|
|
27883
27916
|
return this.request("POST", ENDPOINTS.AGENT_API_KEYS(orgId, agentId));
|
|
27884
27917
|
}
|
|
27918
|
+
/** Revokes all of the agent's active API keys and mints a replacement. */
|
|
27919
|
+
async regenerateAgentApiKey(orgId, agentId) {
|
|
27920
|
+
return this.request("POST", ENDPOINTS.AGENT_API_KEY_REGENERATE(orgId, agentId));
|
|
27921
|
+
}
|
|
27885
27922
|
async revokeAgentApiKey(orgId, agentId, key) {
|
|
27886
27923
|
return this.request("DELETE", ENDPOINTS.AGENT_API_KEY(orgId, agentId, key));
|
|
27887
27924
|
}
|
|
@@ -28053,6 +28090,17 @@ var ParallClient = class _ParallClient {
|
|
|
28053
28090
|
provider_enabled: providerEnabled
|
|
28054
28091
|
});
|
|
28055
28092
|
}
|
|
28093
|
+
/**
|
|
28094
|
+
* Replace the machine's capability set (admin). Primary use: healing a
|
|
28095
|
+
* machine that registered without `browser_provider` during the capability
|
|
28096
|
+
* migration's rolling-deploy window. Removing `agent_host` is rejected by the
|
|
28097
|
+
* server while agents are attached (409 AGENTS_STILL_ATTACHED).
|
|
28098
|
+
*/
|
|
28099
|
+
async patchMachineCapabilities(orgId, machineId, capabilities) {
|
|
28100
|
+
return this.request("PATCH", ENDPOINTS.MACHINE_CAPABILITIES(orgId, machineId), {
|
|
28101
|
+
capabilities
|
|
28102
|
+
});
|
|
28103
|
+
}
|
|
28056
28104
|
/** Get machine-level runtime auth state. */
|
|
28057
28105
|
async getMachineRuntimeAuth(orgId, machineId) {
|
|
28058
28106
|
return this.request("GET", ENDPOINTS.MACHINE_RUNTIME_AUTH(orgId, machineId));
|
|
@@ -28143,6 +28191,22 @@ var ParallClient = class _ParallClient {
|
|
|
28143
28191
|
async postBrowseResponse(requestId, response) {
|
|
28144
28192
|
return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
|
|
28145
28193
|
}
|
|
28194
|
+
/**
|
|
28195
|
+
* `POST /machines/me/browser-profiles/viewer-response/{requestId}` — daemon
|
|
28196
|
+
* reply to a `machine.browser_profile.viewer` control command. Wakes the
|
|
28197
|
+
* api-server request/reply bridge (mirrors {@link postBrowseResponse}).
|
|
28198
|
+
*/
|
|
28199
|
+
async postBrowserProfileViewerResponse(requestId, response) {
|
|
28200
|
+
return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_VIEWER_RESPONSE(requestId), response);
|
|
28201
|
+
}
|
|
28202
|
+
/**
|
|
28203
|
+
* `POST /orgs/{orgId}/browser-profiles/{profileId}/viewer/command` — drive the
|
|
28204
|
+
* hosted browser live viewer (WebRTC signaling + tab nav). Authz: profile
|
|
28205
|
+
* owner or org admin. api-server brokers the command to the host daemon.
|
|
28206
|
+
*/
|
|
28207
|
+
async browserViewerCommand(orgId, profileId, req, opts) {
|
|
28208
|
+
return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, opts);
|
|
28209
|
+
}
|
|
28146
28210
|
async resizeMachine(orgId, machineId, spec) {
|
|
28147
28211
|
return this.request("PATCH", ENDPOINTS.MACHINE_SPEC(orgId, machineId), spec);
|
|
28148
28212
|
}
|
|
@@ -28421,6 +28485,9 @@ var ParallClient = class _ParallClient {
|
|
|
28421
28485
|
async getWikiNodeSections(orgId, wikiId, params) {
|
|
28422
28486
|
return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
|
|
28423
28487
|
}
|
|
28488
|
+
async search(orgId, params, opts) {
|
|
28489
|
+
return this.request("GET", ENDPOINTS.SEARCH(orgId), void 0, params, false, opts);
|
|
28490
|
+
}
|
|
28424
28491
|
async searchWiki(orgId, wikiId, params) {
|
|
28425
28492
|
return this.request("GET", ENDPOINTS.WIKI_SEARCH(orgId, wikiId), void 0, params);
|
|
28426
28493
|
}
|
|
@@ -28516,6 +28583,17 @@ var ParallClient = class _ParallClient {
|
|
|
28516
28583
|
async deleteWikiPathScope(orgId, wikiId, scopeId) {
|
|
28517
28584
|
await this.request("DELETE", ENDPOINTS.WIKI_PATH_SCOPE(orgId, wikiId, scopeId));
|
|
28518
28585
|
}
|
|
28586
|
+
// ---- Wiki Path Restrictions (narrowing ACL — private subtrees) ----
|
|
28587
|
+
async getWikiRestrictions(orgId, wikiId) {
|
|
28588
|
+
const res = await this.request("GET", ENDPOINTS.WIKI_RESTRICTIONS(orgId, wikiId));
|
|
28589
|
+
return res.data;
|
|
28590
|
+
}
|
|
28591
|
+
async createWikiRestriction(orgId, wikiId, data) {
|
|
28592
|
+
return this.request("POST", ENDPOINTS.WIKI_RESTRICTIONS(orgId, wikiId), data);
|
|
28593
|
+
}
|
|
28594
|
+
async deleteWikiRestriction(orgId, wikiId, restrictionId) {
|
|
28595
|
+
await this.request("DELETE", ENDPOINTS.WIKI_RESTRICTION(orgId, wikiId, restrictionId));
|
|
28596
|
+
}
|
|
28519
28597
|
async getWikiAccessStatus(orgId, wikiId, path7) {
|
|
28520
28598
|
return this.request("GET", ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), void 0, path7 ? { path: path7 } : void 0);
|
|
28521
28599
|
}
|
|
@@ -28678,6 +28756,10 @@ var ParallClient = class _ParallClient {
|
|
|
28678
28756
|
async updateClip(orgId, clipId, req) {
|
|
28679
28757
|
return this.request("PATCH", ENDPOINTS.CLIP(orgId, clipId), req);
|
|
28680
28758
|
}
|
|
28759
|
+
/** Atomically set display_name and/or description on multiple clip instances (application metadata). */
|
|
28760
|
+
async bulkUpdateClipMetadata(orgId, req) {
|
|
28761
|
+
return this.request("POST", ENDPOINTS.CLIPS_BULK_METADATA(orgId), req);
|
|
28762
|
+
}
|
|
28681
28763
|
async deleteClip(orgId, clipId) {
|
|
28682
28764
|
await this.request("DELETE", ENDPOINTS.CLIP(orgId, clipId));
|
|
28683
28765
|
}
|
|
@@ -30209,7 +30291,7 @@ var ParallAgentGateway = class {
|
|
|
30209
30291
|
this.dispatchState.mainDispatching = true;
|
|
30210
30292
|
void this.drainMainBuffer();
|
|
30211
30293
|
}
|
|
30212
|
-
}, 5e3);
|
|
30294
|
+
}, 5e3).unref();
|
|
30213
30295
|
}
|
|
30214
30296
|
}
|
|
30215
30297
|
}
|
|
@@ -32407,6 +32489,26 @@ async function waitForOpenClawSessionId(sessionsDir, sessionKey, timeoutMs = 1e4
|
|
|
32407
32489
|
}
|
|
32408
32490
|
function createOpenClawDispatchAdapter(opts) {
|
|
32409
32491
|
const activeStreams = /* @__PURE__ */ new Map();
|
|
32492
|
+
let configFallbackWarned = false;
|
|
32493
|
+
function resolveDispatchConfig(log) {
|
|
32494
|
+
try {
|
|
32495
|
+
const current = opts.core.config?.current?.();
|
|
32496
|
+
if (current && typeof current === "object") {
|
|
32497
|
+
configFallbackWarned = false;
|
|
32498
|
+
return current;
|
|
32499
|
+
}
|
|
32500
|
+
if (!configFallbackWarned) {
|
|
32501
|
+
configFallbackWarned = true;
|
|
32502
|
+
log?.warn?.(`parall[${opts.accountId}]: config.current() unavailable on this host \u2014 dispatching with the startup config snapshot; model switches need a gateway restart until then`);
|
|
32503
|
+
}
|
|
32504
|
+
} catch (err) {
|
|
32505
|
+
if (!configFallbackWarned) {
|
|
32506
|
+
configFallbackWarned = true;
|
|
32507
|
+
log?.warn?.(`parall[${opts.accountId}]: config.current() failed (${String(err)}) \u2014 dispatching with the startup config snapshot; model switches need a gateway restart until then`);
|
|
32508
|
+
}
|
|
32509
|
+
}
|
|
32510
|
+
return opts.cfg;
|
|
32511
|
+
}
|
|
32410
32512
|
return {
|
|
32411
32513
|
abortDispatch(sessionKey) {
|
|
32412
32514
|
activeStreams.get(sessionKey)?.fail(new Error("dispatch deadline exceeded"));
|
|
@@ -32432,7 +32534,7 @@ function createOpenClawDispatchAdapter(opts) {
|
|
|
32432
32534
|
let turnGroupKey = "";
|
|
32433
32535
|
const run = opts.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
32434
32536
|
ctx: buildInboundCtx(opts.core, opts.accountId, event, sessionKey, promptBody, earlierEvents),
|
|
32435
|
-
cfg:
|
|
32537
|
+
cfg: resolveDispatchConfig(context2.log),
|
|
32436
32538
|
dispatcherOptions: {
|
|
32437
32539
|
deliver: async (payload) => {
|
|
32438
32540
|
const replyText = payload.text?.trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/parall",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.0",
|
|
4
4
|
"description": "OpenClaw channel plugin for Parall IM",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"openclaw.plugin.json"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@parall/agent-core": "1.
|
|
20
|
-
"@parall/sdk": "1.
|
|
19
|
+
"@parall/agent-core": "1.35.0",
|
|
20
|
+
"@parall/sdk": "1.35.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^22.0.0",
|
|
@@ -7,129 +7,128 @@ description: "Parall wiki operations: read, search, edit, and propose changes to
|
|
|
7
7
|
|
|
8
8
|
Manage organization wikis via the Parall CLI. Auth is pre-configured.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Mental model
|
|
11
|
+
|
|
12
|
+
Wiki editing works on a **local workspace**: `parall wiki sync` downloads the
|
|
13
|
+
wiki into a directory on disk, you edit those files with your normal file
|
|
14
|
+
tools, then `parall wiki changeset create` uploads the result as a proposal.
|
|
15
|
+
There is no git in the workspace — your edits are detected by diffing against
|
|
16
|
+
the synced baseline.
|
|
17
|
+
|
|
18
|
+
Key facts the commands won't tell you:
|
|
19
|
+
|
|
20
|
+
- **Workspace location is fixed.** Sync output and `parall wiki status` print
|
|
21
|
+
the absolute workspace path (`synced → /path/to/<slug>` / `Mount: ...`).
|
|
22
|
+
Always address wiki files by that absolute path — your shell cwd is usually
|
|
23
|
+
NOT inside the workspace.
|
|
24
|
+
- **Text files only.** Changesets reject binary content. Don't put images or
|
|
25
|
+
archives in the workspace; binary uploads go through the web UI.
|
|
26
|
+
- **`cat`, `search`, `query`, `outline`, and `section` read your local
|
|
27
|
+
workspace copy when it exists** — including your own unproposed edits. Add
|
|
28
|
+
`--remote` to `cat` to read the server version instead.
|
|
29
|
+
- **`search`/`query` are keyword (lexical) matching**, not semantic search.
|
|
30
|
+
`query` additionally ranks whole documents — better for multi-word
|
|
31
|
+
questions; `search` for a single identifier.
|
|
32
|
+
- The `<wiki>` argument is the slug or ID from `parall wiki list`; with a
|
|
33
|
+
single wiki in the org it can be omitted.
|
|
34
|
+
|
|
35
|
+
## Core workflow
|
|
11
36
|
|
|
12
37
|
```bash
|
|
13
|
-
parall wiki
|
|
14
|
-
|
|
15
|
-
parall wiki
|
|
38
|
+
parall wiki sync # 1. get/update files (prints workspace path)
|
|
39
|
+
# 2. read + edit files under the workspace path with standard file tools
|
|
40
|
+
parall wiki diff <wiki> # 3. review exactly what you'll propose
|
|
41
|
+
parall wiki changeset create <wiki> --title "..." # 4. submit
|
|
16
42
|
```
|
|
17
43
|
|
|
18
|
-
|
|
44
|
+
Always sync before starting and always check `diff` before proposing — the
|
|
45
|
+
changeset uploads the full content of every changed file.
|
|
19
46
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
a changeset.
|
|
47
|
+
Unprotected paths auto-merge immediately (`auto_merged: true`); protected
|
|
48
|
+
paths stay open for human review. Follow the returned `next_action` either way.
|
|
23
49
|
|
|
24
|
-
|
|
50
|
+
## Stale base (server moved since your sync)
|
|
25
51
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
This downloads wiki files to a local directory. The output includes the
|
|
31
|
-
**absolute mount path** for each wiki (e.g. `synced → /path/to/workspace/kb`).
|
|
32
|
-
|
|
33
|
-
### Step 2: Find the mount path
|
|
34
|
-
|
|
35
|
-
The sync output JSON contains `synced[].path` — the absolute path where files
|
|
36
|
-
live. You can also check it anytime with:
|
|
52
|
+
If files changed on the server after your last sync, `changeset create` is
|
|
53
|
+
rejected — both by a CLI precheck and by the server (409 `STALE_BASE`) — so
|
|
54
|
+
you can't silently overwrite someone's concurrent edit. Recovery:
|
|
37
55
|
|
|
38
56
|
```bash
|
|
39
|
-
parall wiki
|
|
57
|
+
parall wiki sync # pull latest; your local edits are preserved
|
|
58
|
+
# if a file conflicts, resolve it (see next section)
|
|
59
|
+
parall wiki changeset create <wiki> --title "..."
|
|
40
60
|
```
|
|
41
61
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Use this path with `read`, `write`, and `edit` tools. For example, if the mount
|
|
45
|
-
is `/data/.openclaw/workspace/kb`, then `README.md` is at
|
|
46
|
-
`/data/.openclaw/workspace/kb/README.md`.
|
|
62
|
+
## Sync conflicts
|
|
47
63
|
|
|
48
|
-
|
|
64
|
+
`sync` three-way merges. When both you and the server changed the same file,
|
|
65
|
+
your file is left intact and the upstream copy lands under
|
|
66
|
+
`<workspace>/.parall-wiki/conflicts/`:
|
|
49
67
|
|
|
50
|
-
|
|
68
|
+
| Marker | Meaning |
|
|
69
|
+
|--------|---------|
|
|
70
|
+
| `conflicts/<path>.remote` | Server has different content for `<path>` |
|
|
71
|
+
| `conflicts/<path>.remote-deleted` | Server deleted `<path>`; you still have edits |
|
|
51
72
|
|
|
52
|
-
|
|
73
|
+
All paths below are relative to the workspace root. Pick one:
|
|
53
74
|
|
|
54
75
|
```bash
|
|
55
|
-
|
|
56
|
-
parall
|
|
57
|
-
```
|
|
76
|
+
# Accept upstream (drop your edit):
|
|
77
|
+
cp <workspace>/.parall-wiki/conflicts/<path>.remote <workspace>/<path>
|
|
58
78
|
|
|
59
|
-
|
|
79
|
+
# Keep yours / hand-merge: edit <workspace>/<path> to final content, then
|
|
80
|
+
parall wiki changeset create <wiki> --title "Reconcile <path>"
|
|
60
81
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
parall wiki changeset create <slug> --title "Description of changes"
|
|
82
|
+
# Accept server delete (.remote-deleted only):
|
|
83
|
+
rm <workspace>/<path>
|
|
65
84
|
```
|
|
66
85
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
## Changeset Management
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
parall wiki changeset list <slug> # List all changesets
|
|
75
|
-
parall wiki changeset show <changesetId> <slug> # Show detail + feedback
|
|
76
|
-
parall wiki changeset diff <changesetId> <slug> # Show changeset diff
|
|
77
|
-
```
|
|
86
|
+
Then re-run `parall wiki sync` and delete the used marker file. Conflicts
|
|
87
|
+
exit 0 (they need your decision); `failed[]` entries (download error,
|
|
88
|
+
shape-conflict) exit 1 and retry on the next sync.
|
|
78
89
|
|
|
79
|
-
|
|
90
|
+
## Changesets
|
|
80
91
|
|
|
81
92
|
```bash
|
|
82
|
-
parall wiki changeset
|
|
93
|
+
parall wiki changeset list <wiki>
|
|
94
|
+
parall wiki changeset show <changesetId> <wiki> # status + feedback
|
|
95
|
+
parall wiki changeset diff <changesetId> <wiki>
|
|
96
|
+
parall wiki changeset create <wiki> --update <id> # re-propose after rejection (title inherited)
|
|
83
97
|
```
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
## Handling sync conflicts
|
|
88
|
-
|
|
89
|
-
`wiki sync` runs a three-way merge. When both you and the server changed the
|
|
90
|
-
same file, sync **does not overwrite your work**. It leaves your file intact
|
|
91
|
-
and drops the upstream version under `.parall-wiki/conflicts/`:
|
|
92
|
-
|
|
93
|
-
| Marker | Meaning |
|
|
94
|
-
|--------|---------|
|
|
95
|
-
| `.parall-wiki/conflicts/<path>.remote` | Server has different content (concurrent edit, new file collision, or server changed a file you deleted) |
|
|
96
|
-
| `.parall-wiki/conflicts/<path>.remote-deleted` | Server deleted the file; you still have edits |
|
|
97
|
-
|
|
98
|
-
stderr prints one line per conflict. Recovery:
|
|
99
|
+
Rejected: read the feedback (`show` / `status`), fix the files, re-propose
|
|
100
|
+
with `--update <id>`. Conflict status: `sync`, resolve, then `--update <id>`.
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
parall wiki sync
|
|
104
|
-
```
|
|
102
|
+
Re-propose REPLACES the changeset's previous contents with your current
|
|
103
|
+
workspace diff — to withdraw a file from the proposal, revert it locally
|
|
104
|
+
(restore the synced content) and re-propose; it drops out of the changeset.
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
# edit <path> to final content
|
|
109
|
-
parall wiki diff <slug>
|
|
110
|
-
parall wiki changeset create <slug> --title "Reconcile <path>"
|
|
111
|
-
parall wiki sync # fast-forwards after server merges
|
|
112
|
-
```
|
|
106
|
+
## Discovery & history
|
|
113
107
|
|
|
114
|
-
**Accept server delete** (`.remote-deleted` only):
|
|
115
108
|
```bash
|
|
116
|
-
|
|
117
|
-
parall wiki
|
|
109
|
+
parall wiki query "how is auth configured" <wiki> # multi-word lookup (query FIRST, wiki second)
|
|
110
|
+
parall wiki search "JWT" <wiki> # single keyword (query FIRST, wiki second)
|
|
111
|
+
parall wiki outline <wiki> --path docs/ # heading structure
|
|
112
|
+
parall wiki cat docs/auth.md <wiki> # print a file (--remote for server version)
|
|
113
|
+
parall wiki tree <wiki> # list files
|
|
114
|
+
parall wiki log <wiki> # recent operations
|
|
115
|
+
parall wiki log <wiki> docs/auth.md # per-file commit history
|
|
118
116
|
```
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
next sync. Conflicts exit 0 — they need your decision, not a retry.
|
|
118
|
+
## Permissions
|
|
122
119
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```
|
|
120
|
+
Reads and writes are ACL-checked server-side per path. `parall wiki access
|
|
121
|
+
<path>` shows your level for a path. On a `PERMISSION_DENIED`, errors include
|
|
122
|
+
a `Request approval:` hint — use `parall wiki request-access <path> --reason
|
|
123
|
+
"..."` to file an approval card for a maintainer, then re-sync after approval.
|
|
128
124
|
|
|
129
|
-
##
|
|
125
|
+
## Recovery
|
|
130
126
|
|
|
131
127
|
```bash
|
|
132
|
-
parall wiki
|
|
128
|
+
parall wiki reset <wiki> # discard ALL local edits, restore last-synced state
|
|
129
|
+
parall wiki status <wiki> # local changes + your changesets, anytime
|
|
133
130
|
```
|
|
134
131
|
|
|
135
|
-
CLI success output is JSON
|
|
132
|
+
CLI success output is JSON on stdout (human summary on stderr); errors state
|
|
133
|
+
the reason and the next step — follow them. `parall wiki --help` for the
|
|
134
|
+
full command list.
|
package/src/gateway.ts
CHANGED
|
@@ -195,7 +195,7 @@ async function waitForOpenClawSessionId(
|
|
|
195
195
|
return null;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
function createOpenClawDispatchAdapter(opts: {
|
|
198
|
+
export function createOpenClawDispatchAdapter(opts: {
|
|
199
199
|
core: PluginRuntime;
|
|
200
200
|
cfg: Record<string, unknown>;
|
|
201
201
|
accountId: string;
|
|
@@ -203,6 +203,43 @@ function createOpenClawDispatchAdapter(opts: {
|
|
|
203
203
|
workspaceDir: string;
|
|
204
204
|
}): DispatchAdapter {
|
|
205
205
|
const activeStreams = new Map<string, ReturnType<typeof createRuntimeEventStream>>();
|
|
206
|
+
let configFallbackWarned = false;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Resolve the config for one dispatch. OpenClaw hot-reloads
|
|
210
|
+
* `agents.defaults.model` / `models.providers` WITHOUT restarting channel
|
|
211
|
+
* plugins, so the `ctx.cfg` snapshot captured at startAccount goes stale the
|
|
212
|
+
* moment the operator switches the agent's model — every run would keep the
|
|
213
|
+
* old model until the whole gateway restarts. `core.config.current()` is the
|
|
214
|
+
* host's live (hot-reload-refreshed) config; fall back to the startup
|
|
215
|
+
* snapshot on hosts that predate the API. The fallback warns once per
|
|
216
|
+
* degradation streak (latch resets on success) — silently dispatching with
|
|
217
|
+
* the stale snapshot would recreate the invisible model-switch mismatch this
|
|
218
|
+
* function exists to fix.
|
|
219
|
+
*/
|
|
220
|
+
function resolveDispatchConfig(log?: { warn?: (msg: string) => void }): Record<string, unknown> {
|
|
221
|
+
try {
|
|
222
|
+
const current = opts.core.config?.current?.();
|
|
223
|
+
if (current && typeof current === 'object') {
|
|
224
|
+
configFallbackWarned = false;
|
|
225
|
+
return current as unknown as Record<string, unknown>;
|
|
226
|
+
}
|
|
227
|
+
if (!configFallbackWarned) {
|
|
228
|
+
configFallbackWarned = true;
|
|
229
|
+
log?.warn?.(
|
|
230
|
+
`parall[${opts.accountId}]: config.current() unavailable on this host — dispatching with the startup config snapshot; model switches need a gateway restart until then`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
if (!configFallbackWarned) {
|
|
235
|
+
configFallbackWarned = true;
|
|
236
|
+
log?.warn?.(
|
|
237
|
+
`parall[${opts.accountId}]: config.current() failed (${String(err)}) — dispatching with the startup config snapshot; model switches need a gateway restart until then`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return opts.cfg;
|
|
242
|
+
}
|
|
206
243
|
|
|
207
244
|
return {
|
|
208
245
|
abortDispatch(sessionKey: string): void {
|
|
@@ -238,7 +275,7 @@ function createOpenClawDispatchAdapter(opts: {
|
|
|
238
275
|
promptBody,
|
|
239
276
|
earlierEvents,
|
|
240
277
|
),
|
|
241
|
-
cfg:
|
|
278
|
+
cfg: resolveDispatchConfig(context.log),
|
|
242
279
|
dispatcherOptions: {
|
|
243
280
|
deliver: async (payload: { text?: string }) => {
|
|
244
281
|
const replyText = payload.text?.trim();
|