feat(plugin): fabric-canvas tool; fabric-register env=AGENT_ID only

- bin/fabric-register.mjs: only AGENT_ID is read from the environment;
  --api-key is flag-only (no FABRIC_API_KEY); dropped FABRIC_CENTER_API_BASE
  / FABRIC_IDENTITY_FILE / OPENCLAW_PATH env fallbacks (flags + sensible
  defaults; --center still falls back to openclaw.json).
- New fabric-canvas tool (one tool, four actions): read / share / update /
  close the channel's single pinned canvas. Backed by FabricClient
  get/share/update/removeCanvas (GET/PUT/PATCH/DELETE; empty 2xx body ->
  null). update/close are sharer-only server-side.
- README updated.

Verified: client-level smoke against the running guild —
read(empty→null) → share(v1) → read → update(v2) → close(→null) all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-16 15:28:13 +01:00
parent 26c12533fb
commit aaabb0ddb0
6 changed files with 299 additions and 28 deletions

View File

@@ -14,14 +14,15 @@
* fabric-register --api-key fak_xxx # uses $AGENT_ID
* fabric-register --agent-id echo --api-key fak_xxx
*
* Flags / env:
* --agent-id <id> (or env AGENT_ID; one of them required)
* --api-key <fak_…> (or env FABRIC_API_KEY; required)
* --center <url> (or env FABRIC_CENTER_API_BASE; else openclaw.json;
* else http://localhost:7001/api)
* --identity-file <path> (or env FABRIC_IDENTITY_FILE; else
* ~/.openclaw/fabric-identity.json)
* --openclaw-path <dir> (or env OPENCLAW_PATH; else ~/.openclaw)
* Only AGENT_ID is read from the environment; everything else is a flag.
*
* Flags:
* --agent-id <id> required unless the AGENT_ID env var is set
* --api-key <fak_…> required (flag only — never from the environment)
* --center <url> else openclaw.json channels.fabric.centerApiBase;
* else http://localhost:7001/api
* --identity-file <path> default ~/.openclaw/fabric-identity.json
* --openclaw-path <dir> default ~/.openclaw
* -h | --help
*/
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
@@ -51,14 +52,16 @@ const HELP = `fabric-register — bind an OpenClaw agent to a Fabric Center API
fabric-register --api-key fak_xxx # agent id from $AGENT_ID
fabric-register --agent-id <id> --api-key fak_xxx
--agent-id <id> required unless $AGENT_ID is set
--api-key <fak_…> required (or env FABRIC_API_KEY)
--center <url> Center API base (or env FABRIC_CENTER_API_BASE;
else openclaw.json channels.fabric.centerApiBase;
else http://localhost:7001/api)
--agent-id <id> required unless the AGENT_ID env var is set
--api-key <fak_…> required (flag only — never read from the env)
--center <url> Center API base (else openclaw.json
channels.fabric.centerApiBase; else
http://localhost:7001/api)
--identity-file <path> default ~/.openclaw/fabric-identity.json
--openclaw-path <dir> default ~/.openclaw
-h, --help
Only AGENT_ID is taken from the environment; everything else is a flag.
`;
function fail(msg) {
@@ -81,22 +84,17 @@ async function main() {
fail('no agent id: set the AGENT_ID environment variable or pass --agent-id <id>');
}
const apiKey =
(typeof a['api-key'] === 'string' && a['api-key'].trim()) ||
(process.env.FABRIC_API_KEY && process.env.FABRIC_API_KEY.trim());
if (!apiKey) fail('missing --api-key <fak_…> (or env FABRIC_API_KEY)');
// api key: flag ONLY — never from the environment.
const apiKey = typeof a['api-key'] === 'string' && a['api-key'].trim();
if (!apiKey) fail('missing --api-key <fak_…> (flag only)');
const openclawPath = resolve(
(typeof a['openclaw-path'] === 'string' && a['openclaw-path']) ||
process.env.OPENCLAW_PATH ||
join(homedir(), '.openclaw'),
);
// center api base: flag > env > openclaw.json > default
let center =
(typeof a.center === 'string' && a.center) ||
process.env.FABRIC_CENTER_API_BASE ||
'';
// center api base: flag > openclaw.json > default
let center = (typeof a.center === 'string' && a.center) || '';
if (!center) {
try {
const cfg = JSON.parse(readFileSync(join(openclawPath, 'openclaw.json'), 'utf8'));
@@ -110,7 +108,6 @@ async function main() {
const identityFile = resolve(
(typeof a['identity-file'] === 'string' && a['identity-file']) ||
process.env.FABRIC_IDENTITY_FILE ||
join(openclawPath, 'fabric-identity.json'),
);