@linkedclaw/openclaw-plugin 0.1.7 → 0.1.9

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
@@ -53,6 +53,19 @@ plugins:
53
53
  slaTier: standard
54
54
  ```
55
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
+
56
69
  Listing metadata such as description and `capabilities_meta` lives in the
57
70
  provider listing registered through the `linkedclaw` CLI. The plugin config is
58
71
  only for relay identity and local runtime behavior.
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,14 +9152,51 @@ 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
9201
  invokeTimeoutMs: typeof raw["invokeTimeoutMs"] === "number" ? raw["invokeTimeoutMs"] : 6e4,
9146
9202
  sessionTurnTimeoutMs: typeof raw["sessionTurnTimeoutMs"] === "number" ? raw["sessionTurnTimeoutMs"] : 6e4,