agent-relay 11.8.0 → 11.8.1

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.
@@ -0,0 +1,182 @@
1
+ /**
2
+ * `agent-relay observer` — mint a scoped, read-only observer link.
3
+ *
4
+ * The engine rejects a workspace key (`rk_live_`) on the realtime endpoint, and
5
+ * a workspace key is an administrative credential that has no business in a URL
6
+ * or a terminal transcript. This command mints a scoped `ot_live_` token instead
7
+ * and prints the observer URL built from it, so "let a human follow along" is a
8
+ * single command rather than a hand-rolled API call.
9
+ */
10
+ import { InvalidArgumentError } from 'commander';
11
+ import { createObserverToken, listObserverTokens, revokeObserverToken, } from '@agent-relay/sdk';
12
+ import { DEFAULT_OBSERVER_EXPIRES, DEFAULT_OBSERVER_URL, observerUrl, resolveObserverBaseUrl, } from '../lib/observer-url.js';
13
+ import { printJson, runSdk, withSdkDefaults } from '../lib/sdk-command.js';
14
+ import { resolveBaseUrl, resolveWorkspaceKey } from '../lib/sdk-client.js';
15
+ const MAX_CHANNEL_FILTERS = 50;
16
+ function withObserverDefaults(overrides = {}) {
17
+ return {
18
+ ...withSdkDefaults(overrides),
19
+ createObserverToken,
20
+ listObserverTokens,
21
+ revokeObserverToken,
22
+ now: () => Date.now(),
23
+ randomSuffix: () => Math.random().toString(36).slice(2, 10),
24
+ ...overrides,
25
+ };
26
+ }
27
+ /**
28
+ * Parse a `30m` / `24h` / `7d` duration into milliseconds. Bare digits are
29
+ * rejected rather than guessed at — `--expires 24` is far more likely to mean
30
+ * hours than milliseconds, and silently picking either would be wrong.
31
+ */
32
+ export function parseDuration(value) {
33
+ const match = /^(\d+)([mhd])$/.exec(value.trim());
34
+ if (!match) {
35
+ throw new InvalidArgumentError('Expected a duration like 30m, 24h, or 7d.');
36
+ }
37
+ const amount = Number(match[1]);
38
+ if (amount <= 0) {
39
+ throw new InvalidArgumentError('Expected a positive duration.');
40
+ }
41
+ const unitMs = { m: 60_000, h: 3_600_000, d: 86_400_000 }[match[2]];
42
+ const total = amount * unitMs;
43
+ // The engine caps nothing here, but a token outliving the workspace is a
44
+ // liability rather than a convenience.
45
+ if (total > 90 * 86_400_000) {
46
+ throw new InvalidArgumentError('Expected a duration of 90d or less.');
47
+ }
48
+ return total;
49
+ }
50
+ function parseChannels(value) {
51
+ const names = value
52
+ .split(',')
53
+ .map((name) => name.trim().replace(/^#/, ''))
54
+ .filter(Boolean);
55
+ if (names.length === 0) {
56
+ throw new InvalidArgumentError('Expected at least one channel name.');
57
+ }
58
+ // Collapse duplicates before enforcing the cap: `--channels a,a,a` is one
59
+ // filter, and rejecting it for exceeding a limit it never reaches would be
60
+ // surprising.
61
+ const unique = [...new Set(names)];
62
+ if (unique.length > MAX_CHANNEL_FILTERS) {
63
+ throw new InvalidArgumentError(`Expected at most ${MAX_CHANNEL_FILTERS} channel names.`);
64
+ }
65
+ return unique;
66
+ }
67
+ /**
68
+ * Credentials shared by every observer subcommand. `baseUrl` is spread rather
69
+ * than passed as `undefined` because the SDK options treat an explicit
70
+ * `undefined` and an absent key differently.
71
+ */
72
+ function connection(options) {
73
+ const baseUrl = resolveBaseUrl({ baseUrl: options.baseUrl });
74
+ return {
75
+ workspaceKey: resolveWorkspaceKey({
76
+ workspaceKey: options.workspaceKey,
77
+ }),
78
+ ...(baseUrl ? { baseUrl } : {}),
79
+ };
80
+ }
81
+ function describeToken(token) {
82
+ return {
83
+ id: token.id,
84
+ name: token.name,
85
+ status: token.status,
86
+ expiresAt: token.expiresAt,
87
+ createdAt: token.createdAt,
88
+ ...(token.lastUsedAt === undefined ? {} : { lastUsedAt: token.lastUsedAt }),
89
+ };
90
+ }
91
+ export function registerObserverCommands(program, overrides = {}) {
92
+ const deps = withObserverDefaults(overrides);
93
+ const env = process.env;
94
+ const group = program
95
+ .command('observer')
96
+ .description('Mint a read-only observer link so a human can follow this workspace');
97
+ group
98
+ .option('--workspace-key <key>', 'Workspace key (defaults to RELAY_WORKSPACE_KEY or the active workspace)')
99
+ .option('--base-url <url>', 'Override the engine API base URL (defaults to RELAY_BASE_URL)')
100
+ .option('--observer-url <url>', `Observer dashboard URL (defaults to ${DEFAULT_OBSERVER_URL})`)
101
+ .option('--name <name>', 'Token name (defaults to a generated unique name)')
102
+ .option('--channels <names>', 'Restrict to a comma-separated list of channels', parseChannels)
103
+ .option('--include-dms', 'Include agent DM traffic (excluded by default)')
104
+ .option('--expires <duration>', `Token lifetime, e.g. 30m, 24h, 7d (default ${DEFAULT_OBSERVER_EXPIRES})`, parseDuration)
105
+ .option('--json', 'Output the token metadata and URL as JSON')
106
+ .action(async (options) => {
107
+ await runSdk(deps, async () => {
108
+ const lifetimeMs = options.expires ?? parseDuration(DEFAULT_OBSERVER_EXPIRES);
109
+ const name = options.name?.trim() || `observer-cli-${deps.randomSuffix()}`;
110
+ // Resolve the dashboard URL BEFORE minting. A bad `--observer-url` or
111
+ // `RELAY_OBSERVER_URL` would otherwise mint a live 24-hour token and
112
+ // then throw before printing it, leaving an active credential the
113
+ // caller never saw.
114
+ const observerBase = resolveObserverBaseUrl(options.observerUrl, env);
115
+ const token = await deps.createObserverToken({
116
+ ...connection(options),
117
+ name,
118
+ description: 'Minted by `agent-relay observer` for read-only follow-along',
119
+ filters: {
120
+ includeDms: options.includeDms === true,
121
+ ...(options.channels ? { channelNames: options.channels } : {}),
122
+ },
123
+ expiresAt: new Date(deps.now() + lifetimeMs).toISOString(),
124
+ });
125
+ if (!token.token) {
126
+ throw new Error('Observer token created, but the response did not include token material.');
127
+ }
128
+ const url = observerUrl(observerBase, token.token);
129
+ if (options.json) {
130
+ printJson(deps, { ...describeToken(token), url });
131
+ return;
132
+ }
133
+ deps.log(url);
134
+ deps.log('');
135
+ deps.log(`Read-only. Expires ${token.expiresAt ?? 'never'}.`);
136
+ deps.log(options.includeDms === true ? 'Includes agent DMs.' : 'Channels only — agent DMs are excluded.');
137
+ deps.log(`Revoke with: agent-relay observer revoke ${token.id}`);
138
+ });
139
+ });
140
+ group
141
+ .command('list')
142
+ .description('List observer tokens for this workspace (token material is never shown)')
143
+ .option('--workspace-key <key>', 'Workspace key (defaults to RELAY_WORKSPACE_KEY or the active workspace)')
144
+ .option('--base-url <url>', 'Override the engine API base URL')
145
+ .option('--json', 'Output as JSON')
146
+ .action(async (_options, command) => {
147
+ // The parent `observer` group declares the same flag names, and Commander
148
+ // resolves a repeated option onto the ancestor that declared it first — so
149
+ // the local opts object here is empty and `--json` / `--workspace-key`
150
+ // would be silently dropped. Read the merged view instead.
151
+ const options = command.optsWithGlobals();
152
+ await runSdk(deps, async () => {
153
+ const tokens = await deps.listObserverTokens(connection(options));
154
+ if (options.json) {
155
+ printJson(deps, tokens.map(describeToken));
156
+ return;
157
+ }
158
+ if (tokens.length === 0) {
159
+ deps.log('No observer tokens.');
160
+ return;
161
+ }
162
+ for (const token of tokens) {
163
+ deps.log(`${token.id} ${token.status} expires ${token.expiresAt ?? 'never'} ${token.name}`);
164
+ }
165
+ });
166
+ });
167
+ group
168
+ .command('revoke')
169
+ .description('Revoke an observer token by id')
170
+ .argument('<id>', 'Observer token id')
171
+ .option('--workspace-key <key>', 'Workspace key (defaults to RELAY_WORKSPACE_KEY or the active workspace)')
172
+ .option('--base-url <url>', 'Override the engine API base URL')
173
+ .action(async (id, _options, command) => {
174
+ // See the note on `observer list` — read merged options, not local ones.
175
+ const options = command.optsWithGlobals();
176
+ await runSdk(deps, async () => {
177
+ await deps.revokeObserverToken({ ...connection(options), id });
178
+ deps.log(`Revoked ${id}.`);
179
+ });
180
+ });
181
+ }
182
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../../../src/cli/commands/observer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GAEpB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAuB,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3E,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAW/B,SAAS,oBAAoB,CAC3B,YAAkD,EAAE;IAEpD,OAAO;QACL,GAAG,eAAe,CAAC,SAAS,CAAC;QAC7B,mBAAmB;QACnB,kBAAkB;QAClB,mBAAmB;QACnB,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QACrB,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC3D,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,oBAAoB,CAAC,2CAA2C,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,oBAAoB,CAAC,+BAA+B,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAoB,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAC9B,yEAAyE;IACzE,uCAAuC;IACvC,IAAI,KAAK,GAAG,EAAE,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAI,oBAAoB,CAAC,qCAAqC,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,KAAK,GAAG,KAAK;SAChB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;SAC5C,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,oBAAoB,CAAC,qCAAqC,CAAC,CAAC;IACxE,CAAC;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E,cAAc;IACd,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACxC,MAAM,IAAI,oBAAoB,CAAC,oBAAoB,mBAAmB,iBAAiB,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,OAAgC;IAClD,MAAM,OAAO,GAAG,cAAc,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAA6B,EAAE,CAAC,CAAC;IACnF,OAAO;QACL,YAAY,EAAE,mBAAmB,CAAC;YAChC,YAAY,EAAE,OAAO,CAAC,YAAkC;SACzD,CAAC;QACF,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAyB;IAC9C,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;KAC5E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,OAAgB,EAChB,YAAkD,EAAE;IAEpD,MAAM,IAAI,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAExB,MAAM,KAAK,GAAG,OAAO;SAClB,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,qEAAqE,CAAC,CAAC;IAEtF,KAAK;SACF,MAAM,CACL,uBAAuB,EACvB,yEAAyE,CAC1E;SACA,MAAM,CAAC,kBAAkB,EAAE,+DAA+D,CAAC;SAC3F,MAAM,CAAC,sBAAsB,EAAE,uCAAuC,oBAAoB,GAAG,CAAC;SAC9F,MAAM,CAAC,eAAe,EAAE,kDAAkD,CAAC;SAC3E,MAAM,CAAC,oBAAoB,EAAE,gDAAgD,EAAE,aAAa,CAAC;SAC7F,MAAM,CAAC,eAAe,EAAE,gDAAgD,CAAC;SACzE,MAAM,CACL,sBAAsB,EACtB,8CAA8C,wBAAwB,GAAG,EACzE,aAAa,CACd;SACA,MAAM,CAAC,QAAQ,EAAE,2CAA2C,CAAC;SAC7D,MAAM,CAAC,KAAK,EAAE,OAAgC,EAAE,EAAE;QACjD,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,UAAU,GAAI,OAAO,CAAC,OAA8B,IAAI,aAAa,CAAC,wBAAwB,CAAC,CAAC;YACtG,MAAM,IAAI,GAAI,OAAO,CAAC,IAA2B,EAAE,IAAI,EAAE,IAAI,gBAAgB,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YAEnG,sEAAsE;YACtE,qEAAqE;YACrE,kEAAkE;YAClE,oBAAoB;YACpB,MAAM,YAAY,GAAG,sBAAsB,CAAC,OAAO,CAAC,WAAiC,EAAE,GAAG,CAAC,CAAC;YAE5F,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC;gBAC3C,GAAG,UAAU,CAAC,OAAO,CAAC;gBACtB,IAAI;gBACJ,WAAW,EAAE,6DAA6D;gBAC1E,OAAO,EAAE;oBACP,UAAU,EAAE,OAAO,CAAC,UAAU,KAAK,IAAI;oBACvC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,QAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC5E;gBACD,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,WAAW,EAAE;aAC3D,CAAC,CAAC;YAEH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;YAC9F,CAAC;YAED,MAAM,GAAG,GAAG,WAAW,CAAC,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAEnD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,SAAS,CAAC,IAAI,EAAE,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,sBAAsB,KAAK,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC;YAC9D,IAAI,CAAC,GAAG,CACN,OAAO,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,yCAAyC,CAChG,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,4CAA4C,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,yEAAyE,CAAC;SACtF,MAAM,CACL,uBAAuB,EACvB,yEAAyE,CAC1E;SACA,MAAM,CAAC,kBAAkB,EAAE,kCAAkC,CAAC;SAC9D,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,KAAK,EAAE,QAAiC,EAAE,OAAgB,EAAE,EAAE;QACpE,0EAA0E;QAC1E,2EAA2E;QAC3E,uEAAuE;QACvE,2DAA2D;QAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe,EAA6B,CAAC;QACrE,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;YAElE,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,IAAI,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;gBAChC,OAAO;YACT,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,aAAa,KAAK,CAAC,SAAS,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACjG,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,gCAAgC,CAAC;SAC7C,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;SACrC,MAAM,CACL,uBAAuB,EACvB,yEAAyE,CAC1E;SACA,MAAM,CAAC,kBAAkB,EAAE,kCAAkC,CAAC;SAC9D,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,QAAiC,EAAE,OAAgB,EAAE,EAAE;QAChF,yEAAyE;QACzE,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe,EAA6B,CAAC;QACrE,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,IAAI,CAAC,mBAAmB,CAAC,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Observer dashboard URL construction, shared by the `observer` command group
3
+ * and the `get_observer_url` MCP tool so both produce identical links.
4
+ *
5
+ * The credential rides in the `?key=` query string because that is what the
6
+ * observer dashboard reads. That is precisely why the value must always be a
7
+ * scoped, read-only `ot_live_` token and never a workspace key: query strings
8
+ * land in browser history, referrer headers, and proxy logs.
9
+ */
10
+ /** Where the hosted observer dashboard lives. */
11
+ export declare const DEFAULT_OBSERVER_URL = "https://agentrelay.com/observer";
12
+ /** Default token lifetime for a link meant to be pasted into chat. */
13
+ export declare const DEFAULT_OBSERVER_EXPIRES = "24h";
14
+ /**
15
+ * Resolve the observer dashboard base URL: explicit flag, then
16
+ * `RELAY_OBSERVER_URL` (for self-hosted or staging dashboards), then the
17
+ * hosted default.
18
+ *
19
+ * @param explicit - Value passed on the command line, if any
20
+ * @param env - Environment to read `RELAY_OBSERVER_URL` from
21
+ * @returns A validated absolute URL
22
+ */
23
+ export declare function resolveObserverBaseUrl(explicit: string | undefined, env?: NodeJS.ProcessEnv): string;
24
+ /**
25
+ * Build the observer URL for a token.
26
+ *
27
+ * @param baseUrl - Observer dashboard base URL
28
+ * @param token - Scoped `ot_live_` observer token
29
+ * @returns The full observer URL
30
+ */
31
+ export declare function observerUrl(baseUrl: string, token: string): string;
32
+ //# sourceMappingURL=observer-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer-url.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/observer-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,iDAAiD;AACjD,eAAO,MAAM,oBAAoB,oCAAoC,CAAC;AAEtE,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAE9C;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAmBR;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASlE"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Observer dashboard URL construction, shared by the `observer` command group
3
+ * and the `get_observer_url` MCP tool so both produce identical links.
4
+ *
5
+ * The credential rides in the `?key=` query string because that is what the
6
+ * observer dashboard reads. That is precisely why the value must always be a
7
+ * scoped, read-only `ot_live_` token and never a workspace key: query strings
8
+ * land in browser history, referrer headers, and proxy logs.
9
+ */
10
+ /** Where the hosted observer dashboard lives. */
11
+ export const DEFAULT_OBSERVER_URL = 'https://agentrelay.com/observer';
12
+ /** Default token lifetime for a link meant to be pasted into chat. */
13
+ export const DEFAULT_OBSERVER_EXPIRES = '24h';
14
+ /**
15
+ * Resolve the observer dashboard base URL: explicit flag, then
16
+ * `RELAY_OBSERVER_URL` (for self-hosted or staging dashboards), then the
17
+ * hosted default.
18
+ *
19
+ * @param explicit - Value passed on the command line, if any
20
+ * @param env - Environment to read `RELAY_OBSERVER_URL` from
21
+ * @returns A validated absolute URL
22
+ */
23
+ export function resolveObserverBaseUrl(explicit, env = process.env) {
24
+ const value = explicit?.trim() || env.RELAY_OBSERVER_URL?.trim() || DEFAULT_OBSERVER_URL;
25
+ let parsed;
26
+ try {
27
+ // Fail here rather than emitting a malformed link the caller only discovers
28
+ // after pasting it somewhere public.
29
+ parsed = new URL(value);
30
+ }
31
+ catch {
32
+ throw new Error(`Invalid observer URL: ${value}`);
33
+ }
34
+ // The token is appended to this URL's query string, so the scheme decides
35
+ // where a live credential ends up. `new URL` happily accepts `data:`,
36
+ // `javascript:`, and arbitrary custom schemes; restrict to the two that
37
+ // actually address an observer dashboard so a bad `RELAY_OBSERVER_URL`
38
+ // cannot turn the generated link into a token-exfiltration vector.
39
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
40
+ throw new Error(`Observer URL must be http or https: ${value}`);
41
+ }
42
+ return value;
43
+ }
44
+ /**
45
+ * Build the observer URL for a token.
46
+ *
47
+ * @param baseUrl - Observer dashboard base URL
48
+ * @param token - Scoped `ot_live_` observer token
49
+ * @returns The full observer URL
50
+ */
51
+ export function observerUrl(baseUrl, token) {
52
+ if (!token.startsWith('ot_live_')) {
53
+ // A workspace key in this position would be an administrative credential in
54
+ // a shareable URL — the exact failure this command exists to prevent.
55
+ throw new Error('Observer URLs require a scoped observer token (ot_live_...).');
56
+ }
57
+ const url = new URL(baseUrl);
58
+ url.searchParams.set('key', token);
59
+ return url.toString();
60
+ }
61
+ //# sourceMappingURL=observer-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer-url.js","sourceRoot":"","sources":["../../../src/cli/lib/observer-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,iDAAiD;AACjD,MAAM,CAAC,MAAM,oBAAoB,GAAG,iCAAiC,CAAC;AAEtE,sEAAsE;AACtE,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAA4B,EAC5B,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,KAAK,GAAG,QAAQ,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,oBAAoB,CAAC;IACzF,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,4EAA4E;QAC5E,qCAAqC;QACrC,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,0EAA0E;IAC1E,sEAAsE;IACtE,wEAAwE;IACxE,uEAAuE;IACvE,mEAAmE;IACnE,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,uCAAuC,KAAK,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,KAAa;IACxD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,4EAA4E;QAC5E,sEAAsE;QACtE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnC,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC"}
package/dist/index.cjs CHANGED
@@ -182,6 +182,7 @@ __export(index_exports, {
182
182
  MessageReadPredicate: () => MessageReadPredicate,
183
183
  OBSERVABILITY_UNAVAILABLE: () => OBSERVABILITY_UNAVAILABLE,
184
184
  RELAYCAST_SDK_VERSION: () => RELAYCAST_SDK_VERSION,
185
+ RELAY_OBSERVER_SCOPES: () => RELAY_OBSERVER_SCOPES,
185
186
  RELAY_SERVICE_FAILURE_MESSAGE: () => RELAY_SERVICE_FAILURE_MESSAGE,
186
187
  RelayCapabilityError: () => RelayCapabilityError,
187
188
  RelayError: () => RelayError,
@@ -214,6 +215,7 @@ __export(index_exports, {
214
215
  createLogger: () => createLogger,
215
216
  createNotifyHandler: () => createNotifyHandler,
216
217
  createObserverEventSource: () => createObserverEventSource,
218
+ createObserverToken: () => createObserverToken,
217
219
  createRealtimeClient: () => createRealtimeClient,
218
220
  createRequestEnvelope: () => createRequestEnvelope,
219
221
  createRequestHandler: () => createRequestHandler,
@@ -246,6 +248,7 @@ __export(index_exports, {
246
248
  isPlaceholderTargetFast: () => isPlaceholderTargetFast,
247
249
  isSpawnOrReleaseCommandFast: () => isSpawnOrReleaseCommandFast,
248
250
  isValidAgentName: () => isValidAgentName,
251
+ listObserverTokens: () => listObserverTokens,
249
252
  logAndTraceError: () => logAndTraceError,
250
253
  logRelayHandlerError: () => logRelayHandlerError,
251
254
  mapModelToCli: () => mapModelToCli,
@@ -280,6 +283,7 @@ __export(index_exports, {
280
283
  resolveAgentName: () => resolveAgentName,
281
284
  resolveAgentToken: () => resolveAgentToken,
282
285
  resolveCommand: () => resolveCommand,
286
+ revokeObserverToken: () => revokeObserverToken,
283
287
  routerLog: () => routerLog,
284
288
  safeRelayErrorMessage: () => safeRelayErrorMessage,
285
289
  stripAnsiFast: () => stripAnsiFast,
@@ -295,7 +299,7 @@ __export(index_exports, {
295
299
  module.exports = __toCommonJS(index_exports);
296
300
 
297
301
  // ../../node_modules/@relaycast/sdk/dist/version.js
298
- var SDK_VERSION = "8.1.0";
302
+ var SDK_VERSION = "8.1.3";
299
303
 
300
304
  // ../../node_modules/zod/v4/classic/external.js
301
305
  var external_exports = {};
@@ -21453,6 +21457,45 @@ var RelaycastMessagingClient = class {
21453
21457
 
21454
21458
  // ../sdk/dist/messaging/thin-client.js
21455
21459
  var RELAYCAST_SDK_VERSION = SDK_VERSION;
21460
+ var RELAY_OBSERVER_SCOPES = [
21461
+ "stream:read",
21462
+ "messages:read",
21463
+ "threads:read",
21464
+ "dms:read",
21465
+ "channels:read",
21466
+ "search:read",
21467
+ "agents:read",
21468
+ "nodes:read",
21469
+ "deliveries:read",
21470
+ "activity:read",
21471
+ "files:read",
21472
+ "reactions:read"
21473
+ ];
21474
+ function observerTokenClient(workspaceKey, options) {
21475
+ return new RelayCast(clientConfig(workspaceKey, options));
21476
+ }
21477
+ async function createObserverToken(options) {
21478
+ const client = observerTokenClient(options.workspaceKey, options);
21479
+ return client.observerTokens.create({
21480
+ name: options.name,
21481
+ ...options.description === void 0 ? {} : { description: options.description },
21482
+ scopes: [...options.scopes ?? RELAY_OBSERVER_SCOPES],
21483
+ // `include_dms` defaults to false server-side, but send it explicitly so the
21484
+ // token's stored filters record the decision rather than an absence.
21485
+ filters: {
21486
+ includeDms: options.filters?.includeDms === true,
21487
+ ...options.filters?.channelNames?.length ? { channelNames: options.filters.channelNames } : {},
21488
+ ...options.filters?.agentIds?.length ? { agentIds: options.filters.agentIds } : {}
21489
+ },
21490
+ ...options.expiresAt === void 0 ? {} : { expiresAt: options.expiresAt }
21491
+ });
21492
+ }
21493
+ async function listObserverTokens(options) {
21494
+ return observerTokenClient(options.workspaceKey, options).observerTokens.list();
21495
+ }
21496
+ async function revokeObserverToken(options) {
21497
+ await observerTokenClient(options.workspaceKey, options).observerTokens.revoke(options.id);
21498
+ }
21456
21499
  function pickTelemetryOverrides(options) {
21457
21500
  return {
21458
21501
  ...options.originActor === void 0 ? {} : { originActor: options.originActor },
@@ -24140,6 +24183,7 @@ function generateRequestId(prefix = "") {
24140
24183
  MessageReadPredicate,
24141
24184
  OBSERVABILITY_UNAVAILABLE,
24142
24185
  RELAYCAST_SDK_VERSION,
24186
+ RELAY_OBSERVER_SCOPES,
24143
24187
  RELAY_SERVICE_FAILURE_MESSAGE,
24144
24188
  RelayCapabilityError,
24145
24189
  RelayError,
@@ -24172,6 +24216,7 @@ function generateRequestId(prefix = "") {
24172
24216
  createLogger,
24173
24217
  createNotifyHandler,
24174
24218
  createObserverEventSource,
24219
+ createObserverToken,
24175
24220
  createRealtimeClient,
24176
24221
  createRequestEnvelope,
24177
24222
  createRequestHandler,
@@ -24204,6 +24249,7 @@ function generateRequestId(prefix = "") {
24204
24249
  isPlaceholderTargetFast,
24205
24250
  isSpawnOrReleaseCommandFast,
24206
24251
  isValidAgentName,
24252
+ listObserverTokens,
24207
24253
  logAndTraceError,
24208
24254
  logRelayHandlerError,
24209
24255
  mapModelToCli,
@@ -24238,6 +24284,7 @@ function generateRequestId(prefix = "") {
24238
24284
  resolveAgentName,
24239
24285
  resolveAgentToken,
24240
24286
  resolveCommand,
24287
+ revokeObserverToken,
24241
24288
  routerLog,
24242
24289
  safeRelayErrorMessage,
24243
24290
  stripAnsiFast,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay",
3
- "version": "11.8.0",
3
+ "version": "11.8.1",
4
4
  "description": "Real-time agent-to-agent communication system",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -43,14 +43,14 @@
43
43
  "pack:validate": "npm pack --dry-run"
44
44
  },
45
45
  "dependencies": {
46
- "@agent-relay/cloud": "11.8.0",
47
- "@agent-relay/config": "11.8.0",
48
- "@agent-relay/fleet": "11.8.0",
49
- "@agent-relay/harness-driver": "11.8.0",
50
- "@agent-relay/harnesses": "11.8.0",
51
- "@agent-relay/sdk": "11.8.0",
52
- "@agent-relay/session": "11.8.0",
53
- "@agent-relay/utils": "11.8.0",
46
+ "@agent-relay/cloud": "11.8.1",
47
+ "@agent-relay/config": "11.8.1",
48
+ "@agent-relay/fleet": "11.8.1",
49
+ "@agent-relay/harness-driver": "11.8.1",
50
+ "@agent-relay/harnesses": "11.8.1",
51
+ "@agent-relay/sdk": "11.8.1",
52
+ "@agent-relay/session": "11.8.1",
53
+ "@agent-relay/utils": "11.8.1",
54
54
  "@modelcontextprotocol/sdk": "^1.23.0",
55
55
  "@relayfile/client": "^0.10.27",
56
56
  "@relayflows/cli": "1.0.1",