@masons/agent-network 0.5.0 → 0.5.11

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.
@@ -1,8 +1,13 @@
1
1
  /**
2
- * CLI Setup Path — OAuth 2.0 Device Authorization Grant (RFC 8628).
2
+ * CLI Login Path — OAuth 2.0 Device Authorization Grant (RFC 8628).
3
3
  *
4
- * Implements `configureInteractive()` hook for OpenClaw's
5
- * `openclaw channels login --channel agent-network` command.
4
+ * Implements `login()` hook for OpenClaw's 2026.4.x channel-plugin contract:
5
+ * api.registerChannel({ plugin: { ..., auth: { login } } })
6
+ *
7
+ * Invoked by `openclaw channels login --channel agent-network`. CLI dispatch
8
+ * at `dist/channels-cli-Cc40S0aS.js:85` reads `plugin.auth?.login` and throws
9
+ * `"Channel ... does not support login"` if absent. See WhatsApp + Feishu
10
+ * bundled extensions for reference implementations of the same contract.
6
11
  *
7
12
  * Flow:
8
13
  * 1. POST {idpBaseUrl}/api/auth/device/code with client_id + scope
@@ -11,45 +16,61 @@
11
16
  * → handles `expired_token`, `authorization_pending`, `slow_down`
12
17
  * 3. POST {apiHost}/runtime/v1/onboard with Bearer access_token
13
18
  * → list / select existing agent / create new (terminal prompter)
14
- * 4. Return credentials OpenClaw CLI persists them to openclaw.json
19
+ * 4. Persist credentials directly via `writeCredentials()` into
20
+ * `openclaw.json`. (The CLI does NOT auto-persist after login returns —
21
+ * unlike the legacy `configureInteractive` contract — so the plugin
22
+ * owns the file write; see `runChannelLogin` in channels-cli source.)
15
23
  *
16
- * Pre-migration this file existed as a `setup_codes`-based bespoke flow
17
- * (deleted in fd5568e8). This re-implementation is purely standards-based:
18
- * the IdP is Better Auth's `deviceAuthorization` plugin, which speaks
19
- * RFC 8628; the onboard step is a thin MASONS-specific shim that exchanges
20
- * the user-scope access_token for an agent-scope api_key.
24
+ * History. Pre-migration this file existed as a `setup_codes`-based bespoke
25
+ * flow (deleted in fd5568e8). A device-flow re-implementation landed in 0.5.0
26
+ * under `configureInteractive` mounted at `plugin.setup.configureInteractive`
27
+ * but that mount path is not read by any known OpenClaw version (neither
28
+ * 2026.3.x's top-level `plugin.configureInteractive` nor 2026.4.x's
29
+ * `plugin.auth.login`). 0.5.1 corrects the mount point and adopts the
30
+ * 2026.4.x `{cfg, accountId, runtime, verbose, channelInput}` signature.
21
31
  *
22
- * See #1264 for the design rationale (in particular the "single-driver
23
- * Node semantic" — re-running this flow rotates the api_key and evicts
24
- * any previously-connected Runtime).
32
+ * See #1264 for the design rationale (single-driver Node semantic — re-running
33
+ * this flow rotates the api_key and evicts any previously-connected Runtime).
25
34
  */
26
- interface Prompter {
27
- text(label: string, opts?: {
28
- default?: string;
29
- }): Promise<string>;
30
- confirm(label: string): Promise<boolean>;
31
- select(label: string, choices: string[]): Promise<string>;
32
- }
33
- interface ChannelSetupContext {
34
- configured: boolean;
35
- label: string;
36
- cfg: Record<string, unknown>;
37
- runtime: unknown;
38
- prompter: Prompter;
39
- options: Record<string, unknown>;
35
+ /**
36
+ * Minimal OpenClaw runtime surface passed to `auth.login`. Exposed as
37
+ * `defaultRuntime` in `openclaw/dist/runtime-Dx7oeLYq.js` — plugins get
38
+ * only the output/exit channel, not a prompter (plugins build their own
39
+ * via @clack/prompts, which is what OpenClaw CLI uses internally too).
40
+ */
41
+ interface OpenClawRuntime {
42
+ log(...args: unknown[]): void;
43
+ error(...args: unknown[]): void;
44
+ writeStdout(value: string): void;
45
+ writeJson(value: unknown, space?: number): void;
46
+ exit(code: number): void;
40
47
  }
41
- type SetupResult = "skip" | {
48
+ /**
49
+ * Context passed by OpenClaw CLI to `plugin.auth.login`. Matches the call
50
+ * site at `dist/channels-cli-Cc40S0aS.js:89`:
51
+ * await login({ cfg, accountId, runtime, verbose, channelInput });
52
+ */
53
+ interface AuthLoginContext {
42
54
  cfg: Record<string, unknown>;
43
55
  accountId?: string;
44
- };
56
+ runtime: OpenClawRuntime;
57
+ verbose?: boolean;
58
+ channelInput?: string;
59
+ }
45
60
  /**
46
- * Interactive Device Code Flow for OpenClaw CLI.
61
+ * Channel login hook invoked by `openclaw channels login --channel agent-network`.
47
62
  *
48
- * Called by `openclaw channels login --channel agent-network` (wired via
49
- * `setup: { configureInteractive }` on the channel registration in plugin.ts).
63
+ * Unlike the legacy `configureInteractive` contract (which returned a config
64
+ * object for the CLI to persist), the 2026.4.x `auth.login` contract returns
65
+ * void and expects the plugin to persist any config changes itself. We use
66
+ * `writeCredentials()` in config.ts, which writes `openclaw.json` atomically
67
+ * and bumps the state-cache generation so Layer B's dynamic context re-reads
68
+ * on the next turn.
50
69
  *
51
- * Returns `{ cfg, accountId }` OpenClaw CLI handles writing to openclaw.json.
70
+ * Errors during device flow or onboarding are surfaced via `ctx.runtime.error`
71
+ * (not thrown) so OpenClaw CLI doesn't display a stack trace — matches the
72
+ * WhatsApp/Feishu pattern.
52
73
  */
53
- export declare function configureInteractive(ctx: ChannelSetupContext): Promise<SetupResult>;
74
+ export declare function login(ctx: AuthLoginContext): Promise<void>;
54
75
  export {};
55
76
  //# sourceMappingURL=cli-setup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-setup.d.ts","sourceRoot":"","sources":["../src/cli-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAaH,UAAU,QAAQ;IAChB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D;AAED,UAAU,mBAAmB;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,KAAK,WAAW,GACZ,MAAM,GACN;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA0XzD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,WAAW,CAAC,CA+CtB"}
1
+ {"version":3,"file":"cli-setup.d.ts","sourceRoot":"","sources":["../src/cli-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAiCH;;;;;GAKG;AACH,UAAU,eAAe;IACvB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;GAIG;AACH,UAAU,gBAAgB;IACxB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAobD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDhE"}
package/dist/cli-setup.js CHANGED
@@ -1,8 +1,13 @@
1
1
  /**
2
- * CLI Setup Path — OAuth 2.0 Device Authorization Grant (RFC 8628).
2
+ * CLI Login Path — OAuth 2.0 Device Authorization Grant (RFC 8628).
3
3
  *
4
- * Implements `configureInteractive()` hook for OpenClaw's
5
- * `openclaw channels login --channel agent-network` command.
4
+ * Implements `login()` hook for OpenClaw's 2026.4.x channel-plugin contract:
5
+ * api.registerChannel({ plugin: { ..., auth: { login } } })
6
+ *
7
+ * Invoked by `openclaw channels login --channel agent-network`. CLI dispatch
8
+ * at `dist/channels-cli-Cc40S0aS.js:85` reads `plugin.auth?.login` and throws
9
+ * `"Channel ... does not support login"` if absent. See WhatsApp + Feishu
10
+ * bundled extensions for reference implementations of the same contract.
6
11
  *
7
12
  * Flow:
8
13
  * 1. POST {idpBaseUrl}/api/auth/device/code with client_id + scope
@@ -11,18 +16,24 @@
11
16
  * → handles `expired_token`, `authorization_pending`, `slow_down`
12
17
  * 3. POST {apiHost}/runtime/v1/onboard with Bearer access_token
13
18
  * → list / select existing agent / create new (terminal prompter)
14
- * 4. Return credentials OpenClaw CLI persists them to openclaw.json
19
+ * 4. Persist credentials directly via `writeCredentials()` into
20
+ * `openclaw.json`. (The CLI does NOT auto-persist after login returns —
21
+ * unlike the legacy `configureInteractive` contract — so the plugin
22
+ * owns the file write; see `runChannelLogin` in channels-cli source.)
15
23
  *
16
- * Pre-migration this file existed as a `setup_codes`-based bespoke flow
17
- * (deleted in fd5568e8). This re-implementation is purely standards-based:
18
- * the IdP is Better Auth's `deviceAuthorization` plugin, which speaks
19
- * RFC 8628; the onboard step is a thin MASONS-specific shim that exchanges
20
- * the user-scope access_token for an agent-scope api_key.
24
+ * History. Pre-migration this file existed as a `setup_codes`-based bespoke
25
+ * flow (deleted in fd5568e8). A device-flow re-implementation landed in 0.5.0
26
+ * under `configureInteractive` mounted at `plugin.setup.configureInteractive`
27
+ * but that mount path is not read by any known OpenClaw version (neither
28
+ * 2026.3.x's top-level `plugin.configureInteractive` nor 2026.4.x's
29
+ * `plugin.auth.login`). 0.5.1 corrects the mount point and adopts the
30
+ * 2026.4.x `{cfg, accountId, runtime, verbose, channelInput}` signature.
21
31
  *
22
- * See #1264 for the design rationale (in particular the "single-driver
23
- * Node semantic" — re-running this flow rotates the api_key and evicts
24
- * any previously-connected Runtime).
32
+ * See #1264 for the design rationale (single-driver Node semantic — re-running
33
+ * this flow rotates the api_key and evicts any previously-connected Runtime).
25
34
  */
35
+ import { cancel, confirm as clackConfirm, select as clackSelect, text as clackText, isCancel, } from "@clack/prompts";
36
+ import { writeCredentials } from "./config.js";
26
37
  import { DEFAULT_API_HOST, onboard, PlatformApiError, } from "./platform-client.js";
27
38
  // ---------------------------------------------------------------------------
28
39
  // Constants
@@ -73,6 +84,14 @@ class DeviceFlowError extends Error {
73
84
  }
74
85
  class DeviceFlowExpired extends Error {
75
86
  }
87
+ /**
88
+ * User pressed Ctrl+C at any prompter step. Subclass of `DeviceFlowError`
89
+ * so the existing throw/catch chain still works, but distinguished so the
90
+ * `login` handler can skip the redundant `runtime.error` call (clack's
91
+ * `cancel()` already emitted the canonical "cancelled" UI).
92
+ */
93
+ class CancelError extends DeviceFlowError {
94
+ }
76
95
  /**
77
96
  * Run the RFC 8628 device flow loop. Returns the access_token on success.
78
97
  * On expiration, restarts the loop transparently (user gets a fresh code).
@@ -272,55 +291,107 @@ async function createAgentLoop(platformCfg, accessToken, prompter) {
272
291
  }
273
292
  }
274
293
  // ---------------------------------------------------------------------------
275
- // Public hook
294
+ // Clack-backed Prompter adapter
295
+ // ---------------------------------------------------------------------------
296
+ /**
297
+ * Build a Prompter backed by `@clack/prompts` (the same library OpenClaw CLI
298
+ * uses internally, so the terminal UX is consistent with other channel
299
+ * plugins). Cancel sentinels (user Ctrl+C) are mapped to `DeviceFlowError`
300
+ * so the login function can treat cancels as a clean exit condition.
301
+ */
302
+ function createClackPrompter() {
303
+ const abortOnCancel = (result) => {
304
+ if (isCancel(result)) {
305
+ // Emit clack's canonical cancel UI (bracketed red "cancelled") here so
306
+ // the user sees a clean exit indicator, then throw `CancelError` so the
307
+ // outer `login` catch returns silently (avoids printing the same line
308
+ // twice — UI from `cancel()` + a redundant `runtime.error` afterwards).
309
+ cancel("Setup cancelled.");
310
+ throw new CancelError("Setup cancelled by user.");
311
+ }
312
+ };
313
+ return {
314
+ async text(label) {
315
+ // `text(label, opts?.default)` previously mapped `opts.default` to clack's
316
+ // `placeholder` — but clack's `placeholder` is hint-only (not auto-submitted
317
+ // on empty input), and no current caller passes `opts.default`. Dropped
318
+ // the mapping until a real default-value need surfaces; revisit by
319
+ // implementing `clackText({ message, defaultValue })` then.
320
+ const result = await clackText({ message: label });
321
+ abortOnCancel(result);
322
+ return result;
323
+ },
324
+ async confirm(label) {
325
+ const result = await clackConfirm({ message: label });
326
+ abortOnCancel(result);
327
+ return result;
328
+ },
329
+ async select(label, choices) {
330
+ // Narrow the generic — we use `string` values throughout the flow.
331
+ const result = await clackSelect({
332
+ message: label,
333
+ options: choices.map((choice) => ({ value: choice, label: choice })),
334
+ });
335
+ abortOnCancel(result);
336
+ return result;
337
+ },
338
+ };
339
+ }
340
+ // ---------------------------------------------------------------------------
341
+ // Public hook — `plugin.auth.login` (OpenClaw 2026.4.x contract)
276
342
  // ---------------------------------------------------------------------------
277
343
  /**
278
- * Interactive Device Code Flow for OpenClaw CLI.
344
+ * Channel login hook invoked by `openclaw channels login --channel agent-network`.
279
345
  *
280
- * Called by `openclaw channels login --channel agent-network` (wired via
281
- * `setup: { configureInteractive }` on the channel registration in plugin.ts).
346
+ * Unlike the legacy `configureInteractive` contract (which returned a config
347
+ * object for the CLI to persist), the 2026.4.x `auth.login` contract returns
348
+ * void and expects the plugin to persist any config changes itself. We use
349
+ * `writeCredentials()` in config.ts, which writes `openclaw.json` atomically
350
+ * and bumps the state-cache generation so Layer B's dynamic context re-reads
351
+ * on the next turn.
282
352
  *
283
- * Returns `{ cfg, accountId }` OpenClaw CLI handles writing to openclaw.json.
353
+ * Errors during device flow or onboarding are surfaced via `ctx.runtime.error`
354
+ * (not thrown) so OpenClaw CLI doesn't display a stack trace — matches the
355
+ * WhatsApp/Feishu pattern.
284
356
  */
285
- export async function configureInteractive(ctx) {
286
- const { prompter } = ctx;
357
+ export async function login(ctx) {
287
358
  // Channel-level config (`cfg.apiHost`, `cfg.idpBaseUrl`) — both have
288
359
  // defaults. Existing values from openclaw.json are preserved on re-run.
289
360
  const apiHost = typeof ctx.cfg.apiHost === "string" ? ctx.cfg.apiHost : DEFAULT_API_HOST;
290
361
  const idpBaseUrl = typeof ctx.cfg.idpBaseUrl === "string"
291
362
  ? ctx.cfg.idpBaseUrl
292
363
  : DEFAULT_IDP_BASE_URL;
364
+ const prompter = createClackPrompter();
293
365
  let creds;
294
366
  try {
295
367
  const accessToken = await deviceCodeFlow(idpBaseUrl, prompter);
296
368
  creds = await agentSetup(apiHost, accessToken, prompter);
297
369
  }
298
370
  catch (err) {
371
+ if (err instanceof CancelError) {
372
+ // User pressed Ctrl+C. The clack `cancel()` call inside the prompter
373
+ // adapter already emitted the cancellation UI; nothing else to print.
374
+ return;
375
+ }
299
376
  if (err instanceof DeviceFlowError) {
300
- // Human-friendly surface user reads this and re-runs setup.
301
- await prompter.text(`${err.message}\n\nPress Enter to exit.`);
302
- return "skip";
377
+ // Device-flow / onboard error with a human-friendly message.
378
+ // Print and return cleanly — CLI shows nothing else on a return.
379
+ ctx.runtime.error(err.message);
380
+ return;
303
381
  }
304
382
  if (err instanceof PlatformApiError) {
305
- await prompter.text(`Onboard failed (HTTP ${err.status} ${err.code}): ${err.message}. ` +
306
- "Press Enter to exit.");
307
- return "skip";
383
+ ctx.runtime.error(`Onboard failed (HTTP ${err.status} ${err.code}): ${err.message}`);
384
+ return;
308
385
  }
386
+ // Unknown error — let CLI surface the stack. Surfaces platform bugs
387
+ // that aren't covered by our structured error types.
309
388
  throw err;
310
389
  }
311
- // The openclaw.json schema for an account is { connectorUrl, token }
312
- // (see config-schema.ts). On the wire the field is called `apiKey` to
313
- // disambiguate from "session token" / "JWT" — but locally on disk it's
314
- // persisted as `token` for backward compatibility with existing OpenClaw
315
- // configs and the channel.resolveAccount() reader at channel.ts:485.
316
- return {
317
- cfg: {
318
- accounts: {
319
- default: { connectorUrl: creds.connectorUrl, token: creds.apiKey },
320
- },
321
- apiHost,
322
- idpBaseUrl,
323
- },
324
- accountId: "default",
325
- };
390
+ // Persist: `openclaw.json` schema for an account is `{connectorUrl, token}`
391
+ // (see config-schema.ts). On the wire the onboard response field is
392
+ // `apiKey` to disambiguate from OAuth "session token" / JWT — but locally
393
+ // on disk it's persisted as `token` for backward compatibility with the
394
+ // existing `channel.resolveAccount()` reader.
395
+ await writeCredentials({ connectorUrl: creds.connectorUrl, token: creds.apiKey }, apiHost, idpBaseUrl);
396
+ ctx.runtime.log(`✓ Connected as @${creds.handle}`);
326
397
  }
package/dist/config.d.ts CHANGED
@@ -160,12 +160,16 @@ export interface NetworkCredentials {
160
160
  token: string;
161
161
  }
162
162
  /**
163
- * Write credentials + apiHost to `openclaw.json`.
163
+ * Write credentials + apiHost (+ optional idpBaseUrl) to `openclaw.json`.
164
164
  *
165
- * Atomic: credentials and apiHost are written in a single operation
166
- * to avoid partial-write race conditions.
165
+ * Atomic: all fields are written in a single persistConfig() call to avoid
166
+ * partial-write race conditions.
167
+ *
168
+ * `idpBaseUrl` is written only when provided, mirroring `apiHost`'s pattern —
169
+ * omitted values leave any existing field in place (useful when callers want
170
+ * to refresh credentials without overriding IdP URL).
167
171
  */
168
- export declare function writeCredentials(creds: NetworkCredentials, apiHost?: string): Promise<void>;
172
+ export declare function writeCredentials(creds: NetworkCredentials, apiHost?: string, idpBaseUrl?: string): Promise<void>;
169
173
  /**
170
174
  * Write pending connection target handle.
171
175
  *
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -362,12 +362,16 @@ function ensureNetworkSection(config) {
362
362
  return channels["agent-network"];
363
363
  }
364
364
  /**
365
- * Write credentials + apiHost to `openclaw.json`.
365
+ * Write credentials + apiHost (+ optional idpBaseUrl) to `openclaw.json`.
366
366
  *
367
- * Atomic: credentials and apiHost are written in a single operation
368
- * to avoid partial-write race conditions.
367
+ * Atomic: all fields are written in a single persistConfig() call to avoid
368
+ * partial-write race conditions.
369
+ *
370
+ * `idpBaseUrl` is written only when provided, mirroring `apiHost`'s pattern —
371
+ * omitted values leave any existing field in place (useful when callers want
372
+ * to refresh credentials without overriding IdP URL).
369
373
  */
370
- export async function writeCredentials(creds, apiHost) {
374
+ export async function writeCredentials(creds, apiHost, idpBaseUrl) {
371
375
  const config = await readConfig();
372
376
  const section = ensureNetworkSection(config);
373
377
  section.enabled = true;
@@ -384,6 +388,11 @@ export async function writeCredentials(creds, apiHost) {
384
388
  if (apiHost) {
385
389
  section.apiHost = apiHost;
386
390
  }
391
+ // Write idpBaseUrl if provided (Better Auth IdP URL for device flow).
392
+ // Added in 0.5.1 alongside the plugin.auth.login hook (#1264 follow-up).
393
+ if (idpBaseUrl) {
394
+ section.idpBaseUrl = idpBaseUrl;
395
+ }
387
396
  await persistConfig(config);
388
397
  // Update module-level var — deliberate exception to read/write separation,
389
398
  // same pattern as clearTargetHandle(). Without this, tools fail with
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAiJA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CAoXhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAiJA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaI,iBAAiB;CA6XhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/plugin.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // NOT imported by index.ts to avoid pulling ws/typebox into Next.js app bundles.
4
4
  import pluginManifest from "../openclaw.plugin.json" with { type: "json" };
5
5
  import { agentNetworkChannel, consumeIdentityLinkingNudge } from "./channel.js";
6
- import { configureInteractive } from "./cli-setup.js";
6
+ import { login } from "./cli-setup.js";
7
7
  import { detectPendingState, getConversationManager, getDmScope, getStateCacheGeneration, initPluginRuntime, } from "./config.js";
8
8
  import { getAgentIdentity } from "./environment-context.js";
9
9
  import { ownerNotesQueue } from "./owner-notes.js";
@@ -126,27 +126,33 @@ const plugin = {
126
126
  // Register tools FIRST — they must be available even when the channel
127
127
  // has no credentials yet.
128
128
  registerTools(api);
129
- // Channel registration may fail if no credentials are configured yet.
130
- // This is expected on first run — the user authorizes via the OAuth
131
- // device flow run by `configureInteractive` (CLI command:
132
- // `openclaw channels login --channel agent-network`). After credentials
133
- // are persisted to openclaw.json, OpenClaw hot-reloads and re-runs
134
- // register() — the channel then activates.
129
+ // Register the agent-network channel. The 2026.4.x contract reads
130
+ // `plugin.auth?.login` for `openclaw channels login --channel agent-network`
131
+ // (CLI dispatch in `dist/channels-cli-Cc40S0aS.js:85`). Reference plugins
132
+ // (WhatsApp, Feishu in the OpenClaw bundle) use the same shape.
135
133
  //
136
- // `setup: { configureInteractive }` exposes the CLI hook to OpenClaw.
137
- // Restored in #1264 after the legacy setup flow was deleted in
138
- // fd5568e8 (#1253). The new implementation is RFC 8628 device flow
139
- // against Better Auth at the IdP no MASONS-specific bespoke endpoints.
134
+ // History: 0.5.0 mounted this hook at `plugin.setup.configureInteractive`
135
+ // a path no OpenClaw version reads, so `channels login` never worked.
136
+ // 0.5.1 corrected the mount point. 0.5.2 surfaces register errors
137
+ // instead of silently catching themthe previous "cheap guard"
138
+ // (`catch {}`) hid every kind of register-time failure including the
139
+ // ones we care about (schema validation, missing modules, IO errors).
140
+ //
141
+ // The new contract: log the error visibly AND re-throw, so OpenClaw
142
+ // surfaces the failure to the user rather than running with an
143
+ // un-registered channel that mysteriously "doesn't support login".
144
+ // Per Mingke's review of the 0.5.1 T1 failure (#1264, 2026-04-19).
140
145
  try {
141
146
  api.registerChannel({
142
147
  plugin: {
143
148
  ...agentNetworkChannel,
144
- setup: { configureInteractive },
149
+ auth: { login },
145
150
  },
146
151
  });
147
152
  }
148
- catch {
149
- // Silently ignore — channel will be registered after setup + restart.
153
+ catch (err) {
154
+ console.error("[@masons/agent-network] api.registerChannel failed — channel will be unavailable:", err);
155
+ throw err;
150
156
  }
151
157
  // --- Layer B: Per-turn context injection ---
152
158
  // Injects agent network awareness into every LLM turn via before_prompt_build.
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Plugin version — must match package.json. Validated by prepublishOnly. */
2
- export declare const PLUGIN_VERSION = "0.5.0";
2
+ export declare const PLUGIN_VERSION = "0.5.11";
3
3
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,eAAO,MAAM,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,eAAO,MAAM,cAAc,WAAW,CAAC"}
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Plugin version — must match package.json. Validated by prepublishOnly. */
2
- export const PLUGIN_VERSION = "0.5.0";
2
+ export const PLUGIN_VERSION = "0.5.11";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@masons/agent-network",
3
- "version": "0.5.0",
3
+ "version": "0.5.11",
4
4
  "description": "MASONS plugin for OpenClaw — connect your agent to the agent network",
5
5
  "license": "MIT",
6
6
  "author": "MASONS.ai <hello@masons.ai> (https://masons.ai)",
@@ -58,6 +58,7 @@
58
58
  }
59
59
  },
60
60
  "dependencies": {
61
+ "@clack/prompts": "^1.2.0",
61
62
  "@sinclair/typebox": "^0.34",
62
63
  "debug": "^4.4.3",
63
64
  "ws": "8.18.3"