@linkedclaw/openclaw-plugin 0.1.6 → 0.1.8

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 CHANGED
@@ -42,16 +42,30 @@ plugins:
42
42
  serviceUrl: https://api.linkedclaw.com # services-host for gig-task accept/submit
43
43
  relayUrl: wss://api.linkedclaw.com/ws
44
44
  autoStartProvider: true # default true
45
+ autoAcceptInvokes: true # default true
45
46
  autoAcceptSessions: true # default true
46
47
  autoAcceptGigTasks: false # default false
47
48
  maxConcurrentRuns: 4
48
49
  perRequesterLimit: 2
49
- invokeTimeoutMs: 30000
50
+ invokeTimeoutMs: 60000
50
51
  sessionTurnTimeoutMs: 60000
51
52
  gigTaskTimeoutMs: 300000
52
53
  slaTier: standard
53
54
  ```
54
55
 
56
+ ### Key fallback — `apiKey` may be omitted
57
+
58
+ If `apiKey` is absent from `openclaw.json` (and the `LINKEDCLAW_API_KEY` env
59
+ var is unset), the plugin falls back to the CLI's `~/.linkedclaw/config.yaml`
60
+ — the same file `linkedclaw login` writes. This lets the OAuth login flow keep
61
+ the `lc_…` key out of the agent's hands entirely: the agent edits
62
+ `openclaw.json` with only the non-secret `agentId`, and the plugin borrows the
63
+ key from the file the CLI already wrote. `cloudUrl` and `relayUrl` are borrowed
64
+ from the same file alongside the key, so a key minted against a non-prod cloud
65
+ targets that cloud. Resolution precedence per field:
66
+ `openclaw.json` → `LINKEDCLAW_*` env → `~/.linkedclaw/config.yaml` → default.
67
+ Override the CLI config location with `LINKEDCLAW_CONFIG_DIR`.
68
+
55
69
  Listing metadata such as description and `capabilities_meta` lives in the
56
70
  provider listing registered through the `linkedclaw` CLI. The plugin config is
57
71
  only for relay identity and local runtime behavior.
@@ -71,7 +85,7 @@ When the gateway starts the service, the plugin:
71
85
 
72
86
  1. Opens a WebSocket to the LinkedClaw relay and IDENTIFYs with
73
87
  `{ apiKey, agentId }`.
74
- 2. For each inbound frame, dispatches it into a new subagent run via
88
+ 2. For each accepted inbound frame, dispatches it into a new subagent run via
75
89
  `api.runtime.subagent.run({ sessionKey, message, extraSystemPrompt,
76
90
  deliver: false })`.
77
91
  3. Waits on `waitForRun`, reads the last assistant message, and writes
package/dist/index.d.ts CHANGED
@@ -133,6 +133,7 @@ interface ProviderConfig {
133
133
  }
134
134
  interface PluginRuntimeConfig extends ProviderConfig {
135
135
  autoStartProvider: boolean;
136
+ autoAcceptInvokes: boolean;
136
137
  autoAcceptSessions: boolean;
137
138
  autoAcceptGigTasks: boolean;
138
139
  }
package/dist/index.js CHANGED
@@ -8369,6 +8369,25 @@ var ProviderClient = class {
8369
8369
  throw new Error(`LinkedClaw /health/ready ${res.status}`);
8370
8370
  return HealthStatusSchema.parse(await res.json());
8371
8371
  }
8372
+ /** @implements GET /.well-known/agent.json */
8373
+ async agentCard(params = {}) {
8374
+ const qs = new URLSearchParams();
8375
+ if (params.slug)
8376
+ qs.set("slug", params.slug);
8377
+ if (params.agentId)
8378
+ qs.set("agent_id", params.agentId);
8379
+ const res = await this.fetchImpl(`${this.baseUrl}/.well-known/agent.json?${qs}`, { method: "GET" });
8380
+ if (!res.ok)
8381
+ throw new Error(`LinkedClaw /.well-known/agent.json ${res.status}`);
8382
+ return res.json();
8383
+ }
8384
+ /** @implements GET /.well-known/agent-signing-key.json */
8385
+ async signingKeyJwks() {
8386
+ const res = await this.fetchImpl(`${this.baseUrl}/.well-known/agent-signing-key.json`, { method: "GET" });
8387
+ if (!res.ok)
8388
+ throw new Error(`LinkedClaw /.well-known/agent-signing-key.json ${res.status}`);
8389
+ return res.json();
8390
+ }
8372
8391
  /** @implements GET /metrics */
8373
8392
  async metrics() {
8374
8393
  const res = await this.fetchImpl(`${this.baseUrl}/metrics`, { method: "GET" });
@@ -9133,16 +9152,53 @@ function escapeRegex(s) {
9133
9152
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9134
9153
  }
9135
9154
 
9155
+ // src/cli-config.ts
9156
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
9157
+ import { homedir as homedir2 } from "os";
9158
+ import { join as join2 } from "path";
9159
+ function resolveCliConfigPath() {
9160
+ const dir = process.env["LINKEDCLAW_CONFIG_DIR"] ?? join2(homedir2(), ".linkedclaw");
9161
+ return join2(dir, "config.yaml");
9162
+ }
9163
+ var BORROWABLE_KEYS = /* @__PURE__ */ new Set(["apiKey", "cloudUrl", "relayUrl"]);
9164
+ function readCliConfig(path = resolveCliConfigPath()) {
9165
+ if (!existsSync2(path)) return {};
9166
+ let raw;
9167
+ try {
9168
+ raw = readFileSync2(path, "utf8");
9169
+ } catch {
9170
+ return {};
9171
+ }
9172
+ const out = {};
9173
+ for (const line of raw.split(/\r?\n/)) {
9174
+ const trimmed = line.trim();
9175
+ if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
9176
+ const idx = trimmed.indexOf(":");
9177
+ if (idx === -1) continue;
9178
+ const key = trimmed.slice(0, idx).trim();
9179
+ if (!BORROWABLE_KEYS.has(key)) continue;
9180
+ let value = trimmed.slice(idx + 1).trim();
9181
+ if (value.length === 0) continue;
9182
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
9183
+ value = value.slice(1, -1);
9184
+ }
9185
+ out[key] = value;
9186
+ }
9187
+ return out;
9188
+ }
9189
+
9136
9190
  // src/config.ts
9137
9191
  function parseConfig(raw) {
9192
+ let _cli;
9193
+ const cli = () => _cli ??= readCliConfig();
9138
9194
  const base = {
9139
- cloudUrl: typeof raw["cloudUrl"] === "string" ? raw["cloudUrl"] : process.env["LINKEDCLAW_CLOUD_URL"] ?? DEFAULT_CLOUD_URL,
9195
+ cloudUrl: typeof raw["cloudUrl"] === "string" ? raw["cloudUrl"] : process.env["LINKEDCLAW_CLOUD_URL"] ?? cli().cloudUrl ?? DEFAULT_CLOUD_URL,
9140
9196
  ...typeof raw["serviceUrl"] === "string" ? { serviceUrl: raw["serviceUrl"] } : process.env["LINKEDCLAW_SERVICE_URL"] !== void 0 ? { serviceUrl: process.env["LINKEDCLAW_SERVICE_URL"] } : process.env["LINKEDCLAW_SERVICES_HOST_URL"] !== void 0 ? { serviceUrl: process.env["LINKEDCLAW_SERVICES_HOST_URL"] } : {},
9141
- relayUrl: typeof raw["relayUrl"] === "string" ? raw["relayUrl"] : process.env["LINKEDCLAW_RELAY_URL"] ?? DEFAULT_RELAY_URL,
9197
+ relayUrl: typeof raw["relayUrl"] === "string" ? raw["relayUrl"] : process.env["LINKEDCLAW_RELAY_URL"] ?? cli().relayUrl ?? DEFAULT_RELAY_URL,
9142
9198
  capabilities: Array.isArray(raw["capabilities"]) ? raw["capabilities"] : [],
9143
- ...typeof raw["apiKey"] === "string" ? { apiKey: raw["apiKey"] } : process.env["LINKEDCLAW_API_KEY"] !== void 0 ? { apiKey: process.env["LINKEDCLAW_API_KEY"] } : {},
9199
+ ...typeof raw["apiKey"] === "string" ? { apiKey: raw["apiKey"] } : process.env["LINKEDCLAW_API_KEY"] !== void 0 ? { apiKey: process.env["LINKEDCLAW_API_KEY"] } : cli().apiKey !== void 0 ? { apiKey: cli().apiKey } : {},
9144
9200
  ...typeof raw["agentId"] === "string" ? { agentId: raw["agentId"] } : {},
9145
- invokeTimeoutMs: typeof raw["invokeTimeoutMs"] === "number" ? raw["invokeTimeoutMs"] : 3e4,
9201
+ invokeTimeoutMs: typeof raw["invokeTimeoutMs"] === "number" ? raw["invokeTimeoutMs"] : 6e4,
9146
9202
  sessionTurnTimeoutMs: typeof raw["sessionTurnTimeoutMs"] === "number" ? raw["sessionTurnTimeoutMs"] : 6e4,
9147
9203
  gigTaskTimeoutMs: typeof raw["gigTaskTimeoutMs"] === "number" ? raw["gigTaskTimeoutMs"] : 3e5,
9148
9204
  maxConcurrentRuns: typeof raw["maxConcurrentRuns"] === "number" ? raw["maxConcurrentRuns"] : 4,
@@ -9152,6 +9208,7 @@ function parseConfig(raw) {
9152
9208
  return {
9153
9209
  ...base,
9154
9210
  autoStartProvider: raw["autoStartProvider"] !== false,
9211
+ autoAcceptInvokes: raw["autoAcceptInvokes"] !== false,
9155
9212
  autoAcceptSessions: raw["autoAcceptSessions"] !== false,
9156
9213
  autoAcceptGigTasks: raw["autoAcceptGigTasks"] === true
9157
9214
  };
@@ -9210,6 +9267,12 @@ var SubagentHandler = class {
9210
9267
  }
9211
9268
  // ───── Invoke ─────
9212
9269
  async onInvoke(evt) {
9270
+ if (!this.config.autoAcceptInvokes) {
9271
+ return { error: { code: "provider_manual_mode", message: "invoke handling disabled" } };
9272
+ }
9273
+ if (this.config.capabilities && this.config.capabilities.length > 0 && !this.config.capabilities.includes(evt.capability)) {
9274
+ return { error: { code: "capability_not_supported", message: "capability not supported" } };
9275
+ }
9213
9276
  const sessionKey = this.invokeKey(evt.invoke_id);
9214
9277
  try {
9215
9278
  const run = await this.api.runtime.subagent.run({
@@ -9218,7 +9281,7 @@ var SubagentHandler = class {
9218
9281
  extraSystemPrompt: buildInvokePrompt(evt),
9219
9282
  deliver: false
9220
9283
  });
9221
- const timeoutMs = (evt.timeout_seconds !== void 0 ? evt.timeout_seconds * 1e3 : this.config.invokeTimeoutMs) ?? 3e4;
9284
+ const timeoutMs = (evt.timeout_seconds !== void 0 ? evt.timeout_seconds * 1e3 : this.config.invokeTimeoutMs) ?? 6e4;
9222
9285
  const waited = await this.api.runtime.subagent.waitForRun({ runId: run.runId, timeoutMs });
9223
9286
  if (!isDone(waited.status)) {
9224
9287
  return {
@@ -9486,6 +9549,7 @@ var SUPPORTED_CONFIG_FIELDS = /* @__PURE__ */ new Set([
9486
9549
  "relayUrl",
9487
9550
  "capabilities",
9488
9551
  "autoStartProvider",
9552
+ "autoAcceptInvokes",
9489
9553
  "autoAcceptSessions",
9490
9554
  "autoAcceptGigTasks",
9491
9555
  "invokeTimeoutMs",