@blocks-network/sdk 0.1.45

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.
Files changed (74) hide show
  1. package/README.md +632 -0
  2. package/dist/cli/run.d.ts +39 -0
  3. package/dist/cli/run.js +2 -0
  4. package/dist/config-loader.d.ts +10 -0
  5. package/dist/config-loader.js +1 -0
  6. package/dist/defaults.d.ts +7 -0
  7. package/dist/defaults.js +1 -0
  8. package/dist/env.d.ts +5 -0
  9. package/dist/env.js +1 -0
  10. package/dist/index.d.ts +28 -0
  11. package/dist/index.js +1 -0
  12. package/dist/runtime/agent-auth.d.ts +94 -0
  13. package/dist/runtime/agent-auth.js +1 -0
  14. package/dist/runtime/agent-instance.d.ts +233 -0
  15. package/dist/runtime/agent-instance.js +1 -0
  16. package/dist/runtime/agent-registry.d.ts +270 -0
  17. package/dist/runtime/agent-registry.js +1 -0
  18. package/dist/runtime/artifacts.d.ts +65 -0
  19. package/dist/runtime/artifacts.js +1 -0
  20. package/dist/runtime/auth-provider.d.ts +39 -0
  21. package/dist/runtime/auth-provider.js +1 -0
  22. package/dist/runtime/cdm-config.d.ts +16 -0
  23. package/dist/runtime/cdm-config.js +1 -0
  24. package/dist/runtime/channel-manager.d.ts +125 -0
  25. package/dist/runtime/channel-manager.js +1 -0
  26. package/dist/runtime/consumer-auth.d.ts +112 -0
  27. package/dist/runtime/consumer-auth.js +1 -0
  28. package/dist/runtime/credential-cache.d.ts +40 -0
  29. package/dist/runtime/credential-cache.js +1 -0
  30. package/dist/runtime/file-input.d.ts +37 -0
  31. package/dist/runtime/file-input.js +1 -0
  32. package/dist/runtime/file-upload.d.ts +112 -0
  33. package/dist/runtime/file-upload.js +1 -0
  34. package/dist/runtime/part-helpers.d.ts +52 -0
  35. package/dist/runtime/part-helpers.js +1 -0
  36. package/dist/runtime/protocol-version.d.ts +18 -0
  37. package/dist/runtime/protocol-version.js +1 -0
  38. package/dist/runtime/pubnub-client.d.ts +8 -0
  39. package/dist/runtime/pubnub-client.js +1 -0
  40. package/dist/runtime/pubnub-types.d.ts +119 -0
  41. package/dist/runtime/pubnub-types.js +1 -0
  42. package/dist/runtime/rpc-client.d.ts +45 -0
  43. package/dist/runtime/rpc-client.js +1 -0
  44. package/dist/runtime/stream-context.d.ts +69 -0
  45. package/dist/runtime/stream-context.js +1 -0
  46. package/dist/runtime/stream-ref.d.ts +74 -0
  47. package/dist/runtime/stream-ref.js +1 -0
  48. package/dist/runtime/stream-registry.d.ts +133 -0
  49. package/dist/runtime/stream-registry.js +1 -0
  50. package/dist/runtime/stream-setup-helper.d.ts +87 -0
  51. package/dist/runtime/stream-setup-helper.js +1 -0
  52. package/dist/runtime/task-client.d.ts +259 -0
  53. package/dist/runtime/task-client.js +1 -0
  54. package/dist/runtime/task-session.d.ts +224 -0
  55. package/dist/runtime/task-session.js +1 -0
  56. package/dist/runtime/write-affinity.d.ts +22 -0
  57. package/dist/runtime/write-affinity.js +1 -0
  58. package/dist/runtime/write-affinity.test.d.ts +1 -0
  59. package/dist/runtime/write-affinity.test.js +1 -0
  60. package/dist/stream/bytes.d.ts +14 -0
  61. package/dist/stream/bytes.js +1 -0
  62. package/dist/stream/descriptor.d.ts +47 -0
  63. package/dist/stream/descriptor.js +1 -0
  64. package/dist/stream/index.d.ts +11 -0
  65. package/dist/stream/index.js +1 -0
  66. package/dist/stream/stream-bundle.d.ts +82 -0
  67. package/dist/stream/stream-bundle.js +1 -0
  68. package/dist/stream/stream-client.d.ts +192 -0
  69. package/dist/stream/stream-client.js +1 -0
  70. package/dist/stream/types.d.ts +75 -0
  71. package/dist/stream/types.js +1 -0
  72. package/dist/stream/validate.d.ts +15 -0
  73. package/dist/stream/validate.js +1 -0
  74. package/package.json +74 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Agent Registry - REST-based agent card storage and discovery.
3
+ *
4
+ * All registry operations (register, fetch, get, remove) use the server-side
5
+ * REST endpoints at /api/v1/registry/agents which handle App Context operations,
6
+ * membership indexing, and audit events server-side.
7
+ */
8
+ import { registryAllChannel, registrySkillChannel, registryVisibilityChannel, registryLogChannel, normalizeSkillSlug } from './channel-manager.js';
9
+ import type { AgentAuth } from './agent-auth.js';
10
+ /**
11
+ * Scaling configuration for multi-instance support.
12
+ */
13
+ export interface AgentScaling {
14
+ expectedInstances: number;
15
+ concurrency: number;
16
+ maxPendingBacklog?: number;
17
+ maxRunningTimeSec?: number;
18
+ }
19
+ /**
20
+ * Skill object within an AgentCard.
21
+ */
22
+ export interface AgentSkill {
23
+ id: string;
24
+ name: string;
25
+ description?: string;
26
+ examples?: string[];
27
+ }
28
+ /**
29
+ * Agent card payload (input schema) — authored by developers in agent-card.json.
30
+ *
31
+ * Uses the 9-section structure: identity, capabilities, io, streams,
32
+ * security, services, skills, extensions, runtime.
33
+ */
34
+ export interface AgentCard {
35
+ identity: {
36
+ agentName: string;
37
+ displayName: string;
38
+ description: string;
39
+ version: string;
40
+ provider: {
41
+ organization: string;
42
+ url?: string;
43
+ };
44
+ documentationUrl?: string;
45
+ repositoryUrl?: string;
46
+ iconUrl?: string;
47
+ };
48
+ capabilities: {
49
+ taskKinds: Array<'request' | 'pipe'>;
50
+ };
51
+ io?: {
52
+ inputs?: Array<{
53
+ id: string;
54
+ description?: string;
55
+ contentType: string;
56
+ required: boolean;
57
+ example?: unknown;
58
+ schema?: unknown;
59
+ }>;
60
+ outputs?: Array<{
61
+ id: string;
62
+ description?: string;
63
+ contentType: string;
64
+ guaranteed: boolean;
65
+ example?: unknown;
66
+ schema?: unknown;
67
+ }>;
68
+ };
69
+ streams?: Record<string, {
70
+ direction: string;
71
+ format: string;
72
+ description?: string;
73
+ affinity?: string;
74
+ schema?: unknown;
75
+ outboundSchema?: unknown;
76
+ inboundSchema?: unknown;
77
+ contentType?: string;
78
+ }>;
79
+ security?: {
80
+ encryption?: {
81
+ required: boolean;
82
+ algorithm: string;
83
+ consumerKeyRequired: boolean;
84
+ agentPublicKey?: string;
85
+ agentKeyUrl?: string;
86
+ documentationUrl?: string;
87
+ };
88
+ };
89
+ services?: {
90
+ webhooks?: boolean;
91
+ };
92
+ skills: AgentSkill[];
93
+ extensions?: Record<string, unknown>;
94
+ runtime?: {
95
+ handler?: string;
96
+ handlerExport?: string;
97
+ concurrency?: number;
98
+ expectedInstances?: number;
99
+ maxPendingBacklog?: number;
100
+ maxRunningTimeSec?: number;
101
+ };
102
+ }
103
+ /**
104
+ * Agent card payload (output/public schema) — served by the API.
105
+ * Same as AgentCard minus the `runtime` section.
106
+ */
107
+ export interface OutputAgentCard {
108
+ identity: {
109
+ agentName: string;
110
+ displayName: string;
111
+ description: string;
112
+ version: string;
113
+ provider: {
114
+ organization: string;
115
+ url?: string;
116
+ };
117
+ documentationUrl?: string;
118
+ repositoryUrl?: string;
119
+ iconUrl?: string;
120
+ };
121
+ capabilities: {
122
+ taskKinds: Array<'request' | 'pipe'>;
123
+ };
124
+ io?: AgentCard['io'];
125
+ streams?: AgentCard['streams'];
126
+ security?: AgentCard['security'];
127
+ services?: AgentCard['services'];
128
+ skills: AgentSkill[];
129
+ extensions?: Record<string, unknown>;
130
+ }
131
+ /**
132
+ * Agent entry returned from registry queries.
133
+ */
134
+ export interface AgentEntry {
135
+ /** Agent name identifier */
136
+ agentName: string;
137
+ /** Display name */
138
+ displayName: string;
139
+ /** Human-readable description */
140
+ description?: string;
141
+ /** Skill objects from agent card */
142
+ skills?: AgentSkill[];
143
+ /** Multi-instance scaling configuration */
144
+ scaling?: AgentScaling;
145
+ /** Full agent card payload */
146
+ card?: AgentCard;
147
+ /** Optional card reference (PubNub File ID or URL) */
148
+ cardRef?: string;
149
+ /** Optional short summary for list views */
150
+ cardSummary?: string;
151
+ /** Listing visibility */
152
+ listing: 'playground' | 'private' | 'public';
153
+ /** Creation timestamp (ISO string) */
154
+ createdAt?: string;
155
+ /** Last update timestamp (ISO string) */
156
+ updatedAt?: string;
157
+ }
158
+ /**
159
+ * Options for registering an agent.
160
+ */
161
+ export interface ConnectAgentOptions {
162
+ /** @internal Agent instance identifier — auto-generated by startAgentInstance, not caller-supplied. */
163
+ instanceId: string;
164
+ /** Human-readable description */
165
+ description?: string;
166
+ /** Skill objects advertised by this agent */
167
+ skills?: AgentSkill[];
168
+ /** Multi-instance scaling configuration */
169
+ scaling?: AgentScaling;
170
+ /** Full agent card payload */
171
+ card?: AgentCard;
172
+ /** Optional card reference (PubNub File ID or URL) */
173
+ cardRef?: string;
174
+ /** Optional short summary for list views */
175
+ cardSummary?: string;
176
+ /** Listing visibility (default: 'playground') */
177
+ listing?: 'playground' | 'private' | 'public';
178
+ /** Actor identifier for audit log */
179
+ actor?: string;
180
+ /** Request ID for audit log correlation */
181
+ requestId?: string;
182
+ baseUrl?: string;
183
+ /** AgentAuth instance for API key-based authentication */
184
+ agentAuth?: AgentAuth;
185
+ }
186
+ /**
187
+ * Result of fetching the agent registry.
188
+ */
189
+ export interface AgentRegistryResult {
190
+ agents: AgentEntry[];
191
+ next?: string;
192
+ totalCount?: number;
193
+ }
194
+ /**
195
+ * Audit log event for registry changes.
196
+ */
197
+ export interface RegistryAuditEvent {
198
+ event: 'registry.agent.upsert' | 'registry.agent.delete' | 'registry.membership.add' | 'registry.membership.remove' | 'registry.bootstrap' | 'registry.clear';
199
+ ts: number;
200
+ source: string;
201
+ agentName?: string;
202
+ visibility?: 'public' | 'private';
203
+ skills?: string[];
204
+ cardHash?: string;
205
+ cardRef?: string;
206
+ summaryHash?: string;
207
+ channel?: string;
208
+ action?: string;
209
+ requestId?: string;
210
+ actor?: string;
211
+ etags?: {
212
+ user?: string;
213
+ memberships?: string;
214
+ };
215
+ note?: string;
216
+ }
217
+ /**
218
+ * Detect the host operating system via Node.js os.platform().
219
+ * Returns the raw platform string (e.g. "linux", "darwin", "win32")
220
+ * or "unknown" if detection fails.
221
+ */
222
+ declare function detectDeviceOs(): string;
223
+ /**
224
+ * Register or update an agent in the registry via REST.
225
+ *
226
+ * Sends a POST request to the dedicated registry endpoint which handles
227
+ * all App Context operations (UUID metadata, memberships, audit events)
228
+ * server-side.
229
+ */
230
+ export declare function connectAgent(agentName: string, options: ConnectAgentOptions): Promise<{
231
+ pamToken?: string;
232
+ }>;
233
+ /**
234
+ * Get a single agent by name.
235
+ */
236
+ export declare function getAgent(agentName: string, options?: {
237
+ baseUrl?: string;
238
+ }): Promise<AgentEntry | null>;
239
+ /**
240
+ * Fetch the agent registry (all agents).
241
+ */
242
+ export declare function fetchAgentRegistry(options?: {
243
+ limit?: number;
244
+ cursor?: string;
245
+ baseUrl?: string;
246
+ }): Promise<AgentRegistryResult>;
247
+ /**
248
+ * Fetch agents filtered by skill.
249
+ */
250
+ export declare function fetchAgentsBySkill(skill: string, options?: {
251
+ limit?: number;
252
+ cursor?: string;
253
+ baseUrl?: string;
254
+ }): Promise<AgentRegistryResult>;
255
+ /**
256
+ * Fetch agents filtered by listing type.
257
+ */
258
+ export declare function fetchAgentsByListing(listing: string, options?: {
259
+ limit?: number;
260
+ cursor?: string;
261
+ baseUrl?: string;
262
+ }): Promise<AgentRegistryResult>;
263
+ /**
264
+ * Remove an agent from the registry.
265
+ */
266
+ export declare function removeAgent(agentName: string, options?: {
267
+ baseUrl?: string;
268
+ agentAuth?: AgentAuth;
269
+ }): Promise<boolean>;
270
+ export { detectDeviceOs, registryAllChannel, registrySkillChannel, registryVisibilityChannel, registryLogChannel, normalizeSkillSlug, };
@@ -0,0 +1 @@
1
+ import t from"node:os";import{registryAllChannel as n,registrySkillChannel as e,registryVisibilityChannel as a,registryLogChannel as r,normalizeSkillSlug as s}from"./channel-manager.js";import{getEnv as o}from"../env.js";import{CURRENT_PROTOCOL_VERSION as i,SUPPORTED_PROTOCOL_VERSIONS as c,SDK_VERSION as u,PROTOCOL_VERSION_HEADER as l}from"./protocol-version.js";import{captureAffinity as g,injectAffinity as d}from"./write-affinity.js";function m(){try{return t.platform()}catch{return"unknown"}}async function f(t,n=3,e=500){let a;for(let r=0;r<n;r++)try{return await t()}catch(t){a=t;const s=String(t);if(!(s.includes("ENOTFOUND")||s.includes("ETIMEDOUT")||s.includes("ECONNRESET")||s.includes("NetworkIssues"))||r===n-1)throw t;const o=e*Math.pow(2,r)+100*Math.random();await new Promise(t=>setTimeout(t,o))}throw a}function h(t,n){let e=`${function(t){if(t)return t.replace(/\/+$/,"");throw new Error("[AgentRegistry] baseUrl is required: provide it via options or CDM config")}(n)}/api/v1/registry/agents`;if(t){const n=new URLSearchParams;for(const[e,a]of Object.entries(t))void 0!==a&&""!==a&&n.set(e,a);const a=n.toString();a&&(e+="?"+a)}return e}async function p(t,n){const e=new Headers(n?.headers);e.has(l)||e.set(l,i);const a={};d(a);for(const[t,n]of Object.entries(a))e.set(t,n);const r=await fetch(t,{...n,headers:e});if(g(r.headers),404===r.status)return null;if(!r.ok){const t=(await r.json().catch(()=>({}))).message??`HTTP ${r.status}`;throw new Error(`[AgentRegistry] Request failed: ${t}`)}return{data:await r.json(),status:r.status}}function y(t){return{agentName:t.agentName,displayName:t.name??t.agentName,description:t.description,skills:t.skills,scaling:t.scaling,card:t.card,cardRef:t.cardRef,cardSummary:t.cardSummary,listing:t.listing??"playground",createdAt:t.registeredAt}}export async function connectAgent(t,n){if(!/^[a-zA-Z0-9_]+$/.test(t))throw new Error("agentName must contain only alphanumeric characters and underscores (no hyphens)");h(void 0,n.baseUrl);const e=o("BLOCKS_CLI_VERSION"),a={agentName:t,instanceId:n.instanceId,listing:n.listing,expectedInstances:n.scaling?.expectedInstances,concurrency:n.scaling?.concurrency,maxPendingBacklog:n.scaling?.maxPendingBacklog,maxRunningTimeSec:n.scaling?.maxRunningTimeSec,deviceOs:m(),sdkLanguage:"Node",sdkVersion:u,protocolVersions:[...c],preferredProtocolVersion:i};let r;if(e&&(a.cliVersion=e),!n.agentAuth)throw new Error("[AgentRegistry] agentAuth is required. Set BLOCKS_API_KEY and pass agentAuth to connectAgent.");return r=(await f(async()=>n.agentAuth.init(a))).pamToken??void 0,{pamToken:r}}export async function getAgent(t,n){const e=h({agentName:t},n?.baseUrl),a=await f(async()=>p(e));return a?y(a.data.agent):null}export async function fetchAgentRegistry(t){const n={include:"full"};t?.limit&&(n.limit=String(t.limit)),t?.cursor&&(n.cursor=t.cursor);const e=h(n,t?.baseUrl),a=await f(async()=>p(e));return a?{agents:a.data.agents.map(y),next:a.data.next??void 0,totalCount:a.data.totalCount}:{agents:[],totalCount:0}}export async function fetchAgentsBySkill(t,n){const e={include:"full",skill:t};n?.limit&&(e.limit=String(n.limit)),n?.cursor&&(e.cursor=n.cursor);const a=h(e,n?.baseUrl),r=await f(async()=>p(a));return r?{agents:r.data.agents.map(y),next:r.data.next??void 0,totalCount:r.data.totalCount}:{agents:[],totalCount:0}}export async function fetchAgentsByListing(t,n){const e={include:"full",listing:t};n?.limit&&(e.limit=String(n.limit)),n?.cursor&&(e.cursor=n.cursor);const a=h(e,n?.baseUrl),r=await f(async()=>p(a));return r?{agents:r.data.agents.map(y),next:r.data.next??void 0,totalCount:r.data.totalCount}:{agents:[],totalCount:0}}export async function removeAgent(t,n){const e=h({agentName:t},n?.baseUrl);if(n?.agentAuth){return 404!==(await f(()=>n.agentAuth.authenticatedFetch(e,{method:"DELETE"}))).status}const a=o("BLOCKS_API_KEY");return null!==await f(async()=>p(e,{method:"DELETE",headers:{...a?{Authorization:`Bearer ${a}`}:{}}}))}export{m as detectDeviceOs,n as registryAllChannel,e as registrySkillChannel,a as registryVisibilityChannel,r as registryLogChannel,s as normalizeSkillSlug};
@@ -0,0 +1,65 @@
1
+ import type PubNub from 'pubnub';
2
+ export interface DownloadedArtifact {
3
+ data: Uint8Array;
4
+ mimeType: string;
5
+ fileName?: string;
6
+ }
7
+ export interface ArtifactRef {
8
+ kind: 'inline' | 'file';
9
+ mimeType: string;
10
+ size: number;
11
+ hash?: string;
12
+ /** Base64-encoded data for inline artifacts (kind: 'inline' only) */
13
+ data?: string;
14
+ /** PubNub Files identifier (kind: 'file' only) */
15
+ fileId?: string;
16
+ /** File name (both variants; required for kind: 'file') */
17
+ fileName?: string;
18
+ /** PubNub channel where the file is stored (kind: 'file' only, required).
19
+ * Used with fileId and fileName for pubnub.downloadFile() under PAM. */
20
+ channel?: string;
21
+ expiresAt?: string;
22
+ }
23
+ export declare const shouldInlineArtifact: (sizeBytes: number, inlineLimit?: number) => boolean;
24
+ export interface BuildArtifactInput {
25
+ mimeType: string;
26
+ /** Size in bytes - required if data is not provided */
27
+ size?: number;
28
+ hash?: string;
29
+ /** Raw data for inline artifacts - will be base64 encoded. Accepts
30
+ * `Uint8Array` (includes Node `Buffer` at runtime). */
31
+ data?: Uint8Array;
32
+ /** File reference for uploaded artifacts. Channel is the PubNub channel
33
+ * where the file is stored (required for downloadFile under PAM). */
34
+ file?: {
35
+ id: string;
36
+ name: string;
37
+ channel: string;
38
+ expiresAt?: string;
39
+ };
40
+ /** Original file name (preserved on inline artifacts too). */
41
+ fileName?: string;
42
+ inlineLimit?: number;
43
+ }
44
+ export declare const buildArtifactRef: (input: BuildArtifactInput) => ArtifactRef;
45
+ /**
46
+ * Decode an inline base64 artifact ref into raw bytes.
47
+ * Browser-safe: uses atob() + Uint8Array (no Buffer dependency).
48
+ *
49
+ * The ArtifactRef shape uses top-level `kind: 'inline'` discriminator
50
+ * with `data` as a top-level base64 string field (see ArtifactRef
51
+ * interface and artifact-ref.schema.json).
52
+ */
53
+ export declare function decodeInlineArtifact(ref: ArtifactRef): Uint8Array;
54
+ /**
55
+ * Download an artifact from an ArtifactRef.
56
+ *
57
+ * - Inline artifacts: decode base64 data directly (no PubNub call).
58
+ * - File artifacts: call pubnub.downloadFile() using the ref's
59
+ * channel, fileId, and fileName.
60
+ *
61
+ * Requires a PubNub instance with a valid token granting READ on the
62
+ * artifact's channel. For TaskSession consumers, the T4 read token
63
+ * already grants this.
64
+ */
65
+ export declare function downloadArtifact(ref: ArtifactRef, pubnub: PubNub): Promise<DownloadedArtifact>;
@@ -0,0 +1 @@
1
+ import{DEFAULTS as e}from"../defaults.js";import{bytesToBase64 as i}from"../stream/bytes.js";export const shouldInlineArtifact=(i,n=e.inlineLimitBytes)=>i<=n;export const buildArtifactRef=n=>{const t=n.inlineLimit??e.inlineLimitBytes,a=n.size??n.data?.length??0;if(shouldInlineArtifact(a,t)||!n.file)return{kind:"inline",mimeType:n.mimeType,size:a,hash:n.hash,data:void 0!==n.data?i(n.data):void 0,fileName:n.fileName};const r=n.file;return{kind:"file",mimeType:n.mimeType,size:a,hash:n.hash,fileId:r.id,fileName:r.name,channel:r.channel,expiresAt:r.expiresAt}};export function decodeInlineArtifact(e){if("inline"!==e.kind||!e.data)throw new Error("ArtifactRef is not an inline artifact or has no data");const i=atob(e.data),n=new Uint8Array(i.length);for(let e=0;e<i.length;e++)n[e]=i.charCodeAt(e);return n}export async function downloadArtifact(e,i){if("inline"===e.kind){if(!e.data)throw new Error("Inline ArtifactRef is missing data");return{data:decodeInlineArtifact(e),mimeType:e.mimeType,fileName:e.fileName}}if("file"===e.kind){if(!e.channel||!e.fileId||!e.fileName)throw new Error("File ArtifactRef is missing required fields: channel, fileId, and fileName are all required");const n=await i.downloadFile({channel:e.channel,id:e.fileId,name:e.fileName}),t=n.data??n;let a;if(t instanceof Uint8Array)a=new Uint8Array(t);else if("undefined"!=typeof Blob&&t instanceof Blob){const e=await t.arrayBuffer();a=new Uint8Array(e)}else if("function"==typeof t?.toArrayBuffer){const e=await t.toArrayBuffer();a=new Uint8Array(e)}else{if("function"!=typeof t?.toBuffer)throw new Error("PubNub downloadFile result has no usable data format");{const e=await t.toBuffer();a=new Uint8Array(e)}}return{data:a,mimeType:e.mimeType,fileName:e.fileName}}throw new Error(`Unknown ArtifactRef kind: ${e.kind}`)}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * AuthProvider interface and StaticAuthProvider implementation.
3
+ *
4
+ * All authenticated SDK paths (RPC, file upload, task-read-token) use
5
+ * AuthProvider to obtain the current Authorization header and to handle
6
+ * 401 reactive refresh. Static JWT callers use StaticAuthProvider;
7
+ * ConsumerAuth implements the same interface with proactive + reactive
8
+ * token refresh.
9
+ */
10
+ /**
11
+ * Provides the current Authorization header and handles 401 refresh.
12
+ *
13
+ * Implementations:
14
+ * - StaticAuthProvider: wraps a fixed JWT string, no refresh capability
15
+ * - ConsumerAuth: manages token acquisition and transparent refresh
16
+ */
17
+ export interface AuthProvider {
18
+ /**
19
+ * Returns the current Authorization header value (e.g. "Bearer <jwt>").
20
+ * Returns null if no token is available.
21
+ */
22
+ getAuthHeader(): string | null;
23
+ /**
24
+ * Called by the transport when a request receives a 401 response.
25
+ * Returns true if a fresh token was acquired and the caller should retry.
26
+ * Returns false if no refresh is possible and the 401 should propagate.
27
+ */
28
+ onAuthFailure(): Promise<boolean>;
29
+ }
30
+ /**
31
+ * Wraps a static JWT string. No refresh capability -- onAuthFailure()
32
+ * always returns false.
33
+ */
34
+ export declare class StaticAuthProvider implements AuthProvider {
35
+ private readonly token;
36
+ constructor(token: string);
37
+ getAuthHeader(): string;
38
+ onAuthFailure(): Promise<boolean>;
39
+ }
@@ -0,0 +1 @@
1
+ export class StaticAuthProvider{constructor(t){this.token=t}getAuthHeader(){return`Bearer ${this.token}`}async onAuthFailure(){return!1}}
@@ -0,0 +1,16 @@
1
+ export declare const DEFAULT_CDM_URL = "https://config.blocksnetwork.functions.pdx1.aws.int.ps.pn/config.json";
2
+ export interface CdmKeyset {
3
+ publishKey: string;
4
+ subscribeKey: string;
5
+ }
6
+ export interface CdmApiConfig {
7
+ baseUrl: string;
8
+ clientId?: string;
9
+ }
10
+ export interface CdmConfig {
11
+ playground: CdmKeyset;
12
+ network: CdmKeyset;
13
+ api: CdmApiConfig;
14
+ }
15
+ export type PnEnvironment = 'playground' | 'network';
16
+ export declare function fetchCdmConfig(url?: string): Promise<CdmConfig>;
@@ -0,0 +1 @@
1
+ import{getEnv as n}from"../env.js";export const DEFAULT_CDM_URL="https://config.blocksnetwork.functions.pdx1.aws.int.ps.pn/config.json";export async function fetchCdmConfig(o){const t=o??n("BLOCKS_CDM_URL")??DEFAULT_CDM_URL,s=await fetch(t);if(!s.ok)throw new Error(`CDM config fetch failed: ${s.status} ${s.statusText}`);const r=await s.json();if(!r.playground?.publishKey||!r.playground?.subscribeKey)throw new Error("CDM config missing playground keys");if(!r.network?.publishKey||!r.network?.subscribeKey)throw new Error("CDM config missing network keys");return r}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * ChannelManager - Centralized channel naming utility for agent-name sharding.
3
+ *
4
+ * Channel topology (org-scoped with 3-level constraint):
5
+ * - u.{orgId}.{taskId} - Org-scoped task events (progress, artifacts, terminal, logs)
6
+ * - agent.{agentName}.control - Control plane per agent name
7
+ * - obs.{agentName}.log - Agent-level observability
8
+ *
9
+ * Registry channels (App Context membership indexes):
10
+ * - registry.all - All agents (membership index)
11
+ * - registry.public - Public agents (membership index)
12
+ * - registry.private - Private agents (membership index)
13
+ * - registry.skill.{skill} - Agents with specific skill (membership index)
14
+ * - registry.log - Audit log channel (pub/sub with history)
15
+ *
16
+ * App Context (channel metadata, not pub/sub):
17
+ * - uuid:{agentName} - Agent card stored as User Object
18
+ *
19
+ * All channels respect PubNub's 3-level maximum depth constraint.
20
+ * Subscribe filters are used for agent-name scoping on task channels.
21
+ */
22
+ /**
23
+ * Registry "all" channel - membership index for all agents.
24
+ * Format: registry.all
25
+ */
26
+ export declare const registryAllChannel: () => string;
27
+ /**
28
+ * Registry skill channel - membership index for agents with a skill.
29
+ * Format: registry.skill.{skill}
30
+ *
31
+ * Skill slugs are normalized: lowercase, alphanumeric + `.` or `_`.
32
+ */
33
+ export declare const registrySkillChannel: (skill: string) => string;
34
+ /**
35
+ * Registry visibility channel - membership index for public/private agents.
36
+ * Format: registry.public or registry.private
37
+ */
38
+ export declare const registryVisibilityChannel: (isPublic: boolean) => string;
39
+ /**
40
+ * Registry audit log channel - pub/sub channel for registry change events.
41
+ * Format: registry.log
42
+ */
43
+ export declare const registryLogChannel: () => string;
44
+ /**
45
+ * Normalize a skill string to a stable slug.
46
+ * - Lowercase
47
+ * - Replace non-alphanumeric characters (except `.` and `_`) with `_`
48
+ * - Collapse multiple underscores
49
+ *
50
+ * Examples:
51
+ * - "image-generation" -> "image_generation"
52
+ * - "text.embeddings" -> "text.embeddings"
53
+ * - "Image Generation" -> "image_generation"
54
+ */
55
+ export declare const normalizeSkillSlug: (skill: string) => string;
56
+ /**
57
+ * Validates that an owner ID is not a reserved prefix.
58
+ */
59
+ export declare function validateOwnerId(ownerId: string): boolean;
60
+ export interface ChannelManagerOptions {
61
+ agentName?: string;
62
+ }
63
+ export declare class ChannelManager {
64
+ private readonly agentName;
65
+ constructor(opts?: ChannelManagerOptions);
66
+ getAgentName(): string;
67
+ /**
68
+ * Org-scoped task channel.
69
+ * All task events (progress, artifacts, terminal, logs) publish here.
70
+ * Format: u.{orgId}.{taskId} (3 levels)
71
+ */
72
+ taskChannel(taskId: string, orgId: string): string;
73
+ /**
74
+ * Control channel for agent.
75
+ * Format: agent.{agentName}.control (3 levels)
76
+ */
77
+ controlChannel(agentName?: string): string;
78
+ /**
79
+ * Agent-level observability channel.
80
+ * Format: obs.{agentName}.log (3 levels)
81
+ */
82
+ obsChannel(agentName?: string): string;
83
+ /**
84
+ * Wildcard pattern for org's task channels (for PAM grants).
85
+ * Format: u.{orgId}.* (grants access to all tasks for this org)
86
+ */
87
+ userTaskPattern(orgId: string): string;
88
+ /**
89
+ * Wildcard pattern for agent channels.
90
+ * Format: agent.{agentName}.* (for PAM grants)
91
+ */
92
+ agentWildcard(agentName?: string): string;
93
+ /**
94
+ * Stream data channel for an agent name and stream ID.
95
+ * Format: stream.{agentName}.{streamId} (3 levels).
96
+ *
97
+ * `streamId` is always SDK-derived, never caller-supplied:
98
+ * - Dedicated-affinity streams: `{taskId}-{counter}` (per-task,
99
+ * counter increments per createStream() call within the task).
100
+ * - Shared-affinity streams: the card-declared key (constant
101
+ * across tasks by design).
102
+ */
103
+ streamChannel(streamId: string, agentName?: string): string;
104
+ /**
105
+ * Wildcard pattern for all stream channels of an agent.
106
+ * Format: stream.{agentName}.* (for PAM grants)
107
+ */
108
+ streamWildcard(agentName?: string): string;
109
+ }
110
+ export declare const createChannelManager: (agentName: string) => ChannelManager;
111
+ /**
112
+ * Org-scoped task channel (standalone function).
113
+ * Format: u.{orgId}.{taskId}
114
+ */
115
+ export declare const taskChannel: (taskId: string, orgId: string) => string;
116
+ /**
117
+ * Wildcard pattern for org's task channels (standalone function).
118
+ * Format: u.{orgId}.*
119
+ */
120
+ export declare const userTaskPattern: (orgId: string) => string;
121
+ /**
122
+ * Stream data channel (standalone function).
123
+ * Format: stream.{agentName}.{streamId}
124
+ */
125
+ export declare const streamChannel: (agentName: string, streamId: string) => string;
@@ -0,0 +1 @@
1
+ export const registryAllChannel=()=>"registry.all";export const registrySkillChannel=r=>`registry.skill.${normalizeSkillSlug(r)}`;export const registryVisibilityChannel=r=>r?"registry.public":"registry.private";export const registryLogChannel=()=>"registry.log";export const normalizeSkillSlug=r=>r.toLowerCase().replace(/[^a-z0-9._]/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,"");const r=["agent","obs","sys","u","anonymous","system","stream"];export function validateOwnerId(e){return!(!e||"string"!=typeof e)&&!r.includes(e.toLowerCase())}export class ChannelManager{constructor(r={}){if(!r.agentName)throw new Error("agentName is required for ChannelManager");this.agentName=r.agentName}getAgentName(){return this.agentName}taskChannel(r,e){if(!e)throw new Error("orgId required for task channel");if(!r)throw new Error("taskId required for task channel");return`u.${e}.${r}`}controlChannel(r){return`agent.${r??this.agentName}.control`}obsChannel(r){return`obs.${r??this.agentName}.log`}userTaskPattern(r){if(!r)throw new Error("orgId required for user task pattern");return`u.${r}.*`}agentWildcard(r){return`agent.${r??this.agentName}.*`}streamChannel(r,e){if(!r)throw new Error("streamId required for stream channel");return`stream.${e??this.agentName}.${r}`}streamWildcard(r){return`stream.${r??this.agentName}.*`}}export const createChannelManager=r=>new ChannelManager({agentName:r});export const taskChannel=(r,e)=>{if(!e)throw new Error("orgId required for task channel");if(!r)throw new Error("taskId required for task channel");return`u.${e}.${r}`};export const userTaskPattern=r=>{if(!r)throw new Error("orgId required for user task pattern");return`u.${r}.*`};export const streamChannel=(r,e)=>{if(!r)throw new Error("agentName required for stream channel");if(!e)throw new Error("streamId required for stream channel");return`stream.${r}.${e}`};
@@ -0,0 +1,112 @@
1
+ /**
2
+ * ConsumerAuth -- consumer-side token acquisition and transparent refresh.
3
+ *
4
+ * Implements AuthProvider with three token acquisition modes:
5
+ * Mode 1 (apiKey): exchange API key for consumer JWT via backend
6
+ * Mode 2 (tokenEndpoint): POST to customer-owned proxy endpoint
7
+ * Mode 3 (tokenProvider): call developer-supplied async function
8
+ *
9
+ * Proactive refresh at 80% TTL with exponential backoff on failure.
10
+ * Reactive refresh on 401 via onAuthFailure() with Promise dedup.
11
+ */
12
+ import type { AuthProvider } from './auth-provider.js';
13
+ /** Result from any token acquisition call. */
14
+ export interface TokenResult {
15
+ token: string;
16
+ expiresIn: number;
17
+ userId?: string;
18
+ }
19
+ /**
20
+ * Credentials mode for the fetch call to a token endpoint.
21
+ *
22
+ * Mirrors the DOM/undici `RequestCredentials` union. Inlined here so the
23
+ * Node SDK's TypeScript configuration (no `"DOM"` lib) can reference the
24
+ * type without depending on the DOM ambient typings.
25
+ */
26
+ export type TokenEndpointCredentials = 'omit' | 'same-origin' | 'include';
27
+ /**
28
+ * Configuration for Mode 2 (token endpoint) token acquisition.
29
+ *
30
+ * Accepts either a bare URL string (the legacy form) or a config object
31
+ * with fetch-init overrides (`credentials`, `headers`, `body`). The config
32
+ * form is additive: SDK defaults (`method: 'POST'`, `Content-Type: application/json`,
33
+ * empty-object body) still apply; supplied fields override the defaults.
34
+ *
35
+ * The `body` value (when provided) is passed through `JSON.stringify`, so it
36
+ * must be JSON-serializable.
37
+ */
38
+ export type TokenEndpointConfig = string | {
39
+ url: string;
40
+ credentials?: TokenEndpointCredentials;
41
+ headers?: Record<string, string>;
42
+ body?: unknown;
43
+ };
44
+ /** Options for constructing a ConsumerAuth instance. */
45
+ export interface ConsumerAuthOptions {
46
+ apiKey?: string;
47
+ tokenEndpoint?: TokenEndpointConfig;
48
+ tokenProvider?: () => Promise<TokenResult>;
49
+ baseUrl?: string;
50
+ onAuthError?: (error: Error) => void;
51
+ }
52
+ export declare class ConsumerAuth implements AuthProvider {
53
+ private readonly _apiKey?;
54
+ private readonly _tokenEndpoint?;
55
+ private readonly _tokenProvider?;
56
+ private readonly _baseUrl;
57
+ private readonly _onAuthError?;
58
+ private _token;
59
+ private _refreshToken;
60
+ private _userId;
61
+ private _expiresIn;
62
+ private _destroyed;
63
+ private _refreshTimer;
64
+ private _refreshPromise;
65
+ constructor(options: ConsumerAuthOptions);
66
+ /**
67
+ * Acquire the initial token. Must be called before any other method.
68
+ */
69
+ init(): Promise<void>;
70
+ getAuthHeader(): string | null;
71
+ getUserId(): string | null;
72
+ /**
73
+ * Reactive refresh triggered by a 401 response. Returns true if a new
74
+ * token was acquired. Concurrent callers share a single in-flight
75
+ * refresh via Promise dedup.
76
+ */
77
+ onAuthFailure(): Promise<boolean>;
78
+ /**
79
+ * Stop proactive refresh timer. Does NOT clear the token -- active
80
+ * sessions can still use the last-known token for its remaining TTL.
81
+ */
82
+ destroy(): void;
83
+ private _initApiKey;
84
+ private _refreshApiKey;
85
+ private _initTokenEndpoint;
86
+ /**
87
+ * Mode-2 token-endpoint traffic goes to a customer-owned proxy, not the
88
+ * Blocks backend, so `X-Write-Affinity` is not exchanged in either
89
+ * direction: echoing it outbound would leak internal routing state to a
90
+ * third party, and capturing it inbound would let the proxy dictate this
91
+ * SDK's DB routing. Affinity lives on SDK -> Blocks calls only.
92
+ */
93
+ private _fetchTokenEndpoint;
94
+ private _initTokenProvider;
95
+ private _applyTokenResult;
96
+ /**
97
+ * Perform a single refresh attempt based on the active mode.
98
+ */
99
+ private _refreshOnce;
100
+ /**
101
+ * Deduplicated refresh: only one concurrent refresh runs at a time.
102
+ * Additional callers share the in-flight Promise.
103
+ */
104
+ private _doRefreshDedup;
105
+ /**
106
+ * Schedule proactive refresh at 80% of expiresIn. On failure, retry
107
+ * with exponential backoff (max 3 retries). On permanent failure,
108
+ * call onAuthError and stop scheduling.
109
+ */
110
+ private _scheduleRefresh;
111
+ private _proactiveRefreshWithRetry;
112
+ }
@@ -0,0 +1 @@
1
+ import{CURRENT_PROTOCOL_VERSION as e,PROTOCOL_VERSION_HEADER as t}from"./protocol-version.js";import{captureAffinity as r,injectAffinity as s}from"./write-affinity.js";const i="API_KEY_INVALID";export class ConsumerAuth{constructor(e){this._token=null,this._refreshToken=null,this._userId=null,this._expiresIn=0,this._destroyed=!1,this._refreshTimer=null,this._refreshPromise=null,this._apiKey=e.apiKey,this._tokenEndpoint=e.tokenEndpoint,this._tokenProvider=e.tokenProvider,this._baseUrl=(e.baseUrl??"").replace(/\/+$/,""),this._onAuthError=e.onAuthError}async init(){if(this._apiKey)await this._initApiKey();else if(this._tokenEndpoint)await this._initTokenEndpoint();else{if(!this._tokenProvider)throw new Error("ConsumerAuth requires one of: apiKey, tokenEndpoint, or tokenProvider");await this._initTokenProvider()}this._scheduleRefresh()}getAuthHeader(){return this._token?`Bearer ${this._token}`:null}getUserId(){return this._userId}async onAuthFailure(){return!this._destroyed&&this._doRefreshDedup()}destroy(){this._destroyed=!0,this._refreshTimer&&(clearTimeout(this._refreshTimer),this._refreshTimer=null)}async _initApiKey(){const i=`${this._baseUrl}/api/v1/auth/agent/consumer-token`,n={"Content-Type":"application/json",[t]:e};s(n);const o=await fetch(i,{method:"POST",headers:n,body:JSON.stringify({apiKey:this._apiKey})});if(r(o.headers),!o.ok){const e=await o.text().catch(()=>"");throw new Error(`consumer-token failed: HTTP ${o.status}${e?` ${e}`:""}`)}const h=await o.json();this._token=h.accessToken,this._refreshToken=h.refreshToken,this._expiresIn=h.expiresIn,this._userId=h.userId}async _refreshApiKey(){const e=`${this._baseUrl}/api/v1/auth/agent/refresh`,t={Authorization:`Bearer ${this._apiKey}`,"Content-Type":"application/json"};s(t);const n=await fetch(e,{method:"POST",headers:t,body:JSON.stringify({refreshToken:this._refreshToken})});if(r(n.headers),!n.ok){const e=await n.json().catch(()=>({}));if("REFRESH_TOKEN_INVALID"===e.code)return void await this._initApiKey();if(e.code===i)throw new Error(`API key invalid or revoked: ${e.error??i}`);throw new Error(`token refresh failed: ${e.error??`HTTP ${n.status}`}`)}const o=await n.json();this._token=o.accessToken,this._refreshToken=o.refreshToken,"number"==typeof o.expiresIn&&(this._expiresIn=o.expiresIn)}async _initTokenEndpoint(){const e=await this._fetchTokenEndpoint();this._applyTokenResult(e)}async _fetchTokenEndpoint(){const e=this._tokenEndpoint,t="string"==typeof e,r=t?e:e.url;if("string"!=typeof r||!r)throw new Error("TokenEndpointConfig.url is required and must be a non-empty string");const s=t?void 0:e.credentials,i=t?{}:e.headers??{},n=t?{}:e.body??{},o={method:"POST",headers:{"Content-Type":"application/json",...i},body:JSON.stringify(n)};void 0!==s&&(o.credentials=s);const h=await fetch(r,o);if(!h.ok){const e=await h.text().catch(()=>"");throw new Error(`token endpoint failed: HTTP ${h.status}${e?` ${e}`:""}`)}return h.json()}async _initTokenProvider(){const e=await this._tokenProvider();this._applyTokenResult(e)}_applyTokenResult(e){this._token=e.token,this._expiresIn=e.expiresIn,void 0!==e.userId&&(this._userId=e.userId)}async _refreshOnce(){if(this._apiKey)await this._refreshApiKey();else if(this._tokenEndpoint){const e=await this._fetchTokenEndpoint();this._applyTokenResult(e)}else if(this._tokenProvider){const e=await this._tokenProvider();this._applyTokenResult(e)}}_doRefreshDedup(){return this._refreshPromise||(this._refreshPromise=this._refreshOnce().then(()=>(this._scheduleRefresh(),!0)).catch(()=>!1).finally(()=>{this._refreshPromise=null})),this._refreshPromise}_scheduleRefresh(){if(this._destroyed||this._expiresIn<=0)return;this._refreshTimer&&clearTimeout(this._refreshTimer);const e=1e3*this._expiresIn*.8;this._refreshTimer=setTimeout(()=>{this._refreshTimer=null,this._proactiveRefreshWithRetry(0)},e)}async _proactiveRefreshWithRetry(e){if(!this._destroyed)try{await this._refreshOnce(),this._scheduleRefresh()}catch(t){if(e<2){const t=Math.min(5e3*Math.pow(2,e),3e4);this._destroyed||(this._refreshTimer=setTimeout(()=>{this._refreshTimer=null,this._proactiveRefreshWithRetry(e+1)},t))}else{const e=t instanceof Error?t:new Error(String(t));this._onAuthError?this._onAuthError(e):console.warn("[ConsumerAuth] proactive refresh permanently failed:",e.message)}}}}