@secondlayer/sdk 0.10.2 → 1.0.0

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.
@@ -4,18 +4,88 @@ interface SecondLayerOptions {
4
4
  baseUrl: string;
5
5
  /** Bearer token for authenticated requests. */
6
6
  apiKey?: string;
7
+ /** Deploy origin label sent as `x-sl-origin` (telemetry). Defaults to `cli`. */
8
+ origin?: "cli" | "mcp" | "session";
7
9
  }
8
10
  declare abstract class BaseClient {
9
11
  protected baseUrl: string;
10
12
  protected apiKey?: string;
13
+ protected origin: "cli" | "mcp" | "session";
11
14
  constructor(options?: Partial<SecondLayerOptions>);
12
15
  static authHeaders(apiKey?: string): Record<string, string>;
13
16
  protected request<T>(method: string, path: string, body?: unknown): Promise<T>;
14
17
  }
18
+ interface WorkflowSource {
19
+ name: string;
20
+ version: string;
21
+ sourceCode: string | null;
22
+ readOnly: boolean;
23
+ reason?: string;
24
+ updatedAt: string;
25
+ }
26
+ interface WorkflowStepEvent {
27
+ id: string;
28
+ stepIndex: number;
29
+ stepId: string;
30
+ stepType: string;
31
+ status: string;
32
+ output?: unknown;
33
+ error: string | null;
34
+ retryCount: number;
35
+ aiTokensUsed: number;
36
+ startedAt: string | null;
37
+ completedAt: string | null;
38
+ durationMs: number | null;
39
+ ts: string;
40
+ }
41
+ interface WorkflowRunDoneEvent {
42
+ runId: string;
43
+ status: string;
44
+ error?: string | null;
45
+ completedAt?: string | null;
46
+ }
47
+ type WorkflowTailEvent = {
48
+ type: "step"
49
+ step: WorkflowStepEvent
50
+ } | {
51
+ type: "done"
52
+ done: WorkflowRunDoneEvent
53
+ } | {
54
+ type: "heartbeat"
55
+ ts: string
56
+ } | {
57
+ type: "timeout"
58
+ message: string
59
+ };
60
+ interface DeployDryRunResponse {
61
+ valid: boolean;
62
+ validation?: {
63
+ name: string
64
+ triggerType: string
65
+ };
66
+ bundleSize: number;
67
+ error?: string;
68
+ }
69
+ interface DeployResponse {
70
+ action: "created" | "updated";
71
+ workflowId: string;
72
+ version: string;
73
+ message: string;
74
+ }
75
+ interface BundleWorkflowResponse {
76
+ ok: true;
77
+ name: string;
78
+ trigger: Record<string, unknown>;
79
+ handlerCode: string;
80
+ sourceCode: string;
81
+ retries: Record<string, unknown> | null;
82
+ timeout: number | null;
83
+ bundleSize: number;
84
+ }
15
85
  interface WorkflowSummary {
16
86
  name: string;
17
87
  status: "active" | "paused";
18
- triggerType: "event" | "stream" | "schedule" | "manual";
88
+ triggerType: "event" | "schedule" | "manual";
19
89
  createdAt: string;
20
90
  updatedAt: string;
21
91
  }
@@ -43,13 +113,63 @@ declare class Workflows extends BaseClient {
43
113
  name: string
44
114
  trigger: Record<string, unknown>
45
115
  handlerCode: string
116
+ sourceCode?: string
117
+ expectedVersion?: string
46
118
  retries?: Record<string, unknown>
47
119
  timeout?: number
48
- }): Promise<{
49
- action: string
50
- workflowId: string
51
- message: string
120
+ clientRequestId?: string
121
+ } & {
122
+ dryRun?: false
123
+ }): Promise<DeployResponse>;
124
+ deploy(data: {
125
+ name: string
126
+ trigger: Record<string, unknown>
127
+ handlerCode: string
128
+ sourceCode?: string
129
+ expectedVersion?: string
130
+ retries?: Record<string, unknown>
131
+ timeout?: number
132
+ dryRun: true
133
+ }): Promise<DeployDryRunResponse>;
134
+ getSource(name: string): Promise<WorkflowSource>;
135
+ /**
136
+ * Bundle a TypeScript workflow source on the server. Used by the web chat
137
+ * authoring loop so Vercel's serverless runtime doesn't have to run esbuild.
138
+ * CLI and MCP still bundle locally — this method is for clients that can't
139
+ * install `@secondlayer/bundler` directly (e.g. browser tooling, edge
140
+ * functions).
141
+ */
142
+ bundle(data: {
143
+ code: string
144
+ }): Promise<BundleWorkflowResponse>;
145
+ pauseAll(): Promise<{
146
+ paused: number
147
+ workflows: Array<{
148
+ name: string
149
+ version: string
150
+ status: string
151
+ }>
152
+ }>;
153
+ cancelRun(runId: string): Promise<{
154
+ runId: string
155
+ status: string
156
+ cancelled: boolean
157
+ completedAt?: string
158
+ message?: string
159
+ }>;
160
+ rollback(name: string, toVersion?: string): Promise<{
161
+ action: "rolled-back"
162
+ name: string
163
+ fromVersion: string
164
+ restoredFromVersion: string
165
+ version: string
52
166
  }>;
167
+ /**
168
+ * Subscribe to a workflow run's server-sent event stream. Resolves when the
169
+ * run completes, times out, or the signal is aborted. Throws on HTTP errors
170
+ * opening the stream.
171
+ */
172
+ streamRun(name: string, runId: string, onEvent: (event: WorkflowTailEvent) => void, signal?: AbortSignal): Promise<void>;
53
173
  list(): Promise<{
54
174
  workflows: WorkflowSummary[]
55
175
  }>;
@@ -1,35 +1,51 @@
1
1
  // src/errors.ts
2
2
  class ApiError extends Error {
3
3
  status;
4
- constructor(status, message) {
4
+ body;
5
+ constructor(status, message, body) {
5
6
  super(message);
6
7
  this.status = status;
8
+ this.body = body;
7
9
  this.name = "ApiError";
8
10
  }
9
11
  }
10
12
 
13
+ class VersionConflictError extends ApiError {
14
+ currentVersion;
15
+ expectedVersion;
16
+ constructor(currentVersion, expectedVersion, message = `Version conflict: expected ${expectedVersion}, current ${currentVersion}`) {
17
+ super(409, message, { currentVersion, expectedVersion });
18
+ this.currentVersion = currentVersion;
19
+ this.expectedVersion = expectedVersion;
20
+ this.name = "VersionConflictError";
21
+ }
22
+ }
23
+
11
24
  // src/base.ts
12
25
  var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
13
26
 
14
27
  class BaseClient {
15
28
  baseUrl;
16
29
  apiKey;
30
+ origin;
17
31
  constructor(options = {}) {
18
32
  this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
19
33
  this.apiKey = options.apiKey;
34
+ this.origin = options.origin ?? "cli";
20
35
  }
21
36
  static authHeaders(apiKey) {
22
37
  const headers = {
23
38
  "Content-Type": "application/json"
24
39
  };
25
40
  if (apiKey) {
26
- headers["Authorization"] = `Bearer ${apiKey}`;
41
+ headers.Authorization = `Bearer ${apiKey}`;
27
42
  }
28
43
  return headers;
29
44
  }
30
45
  async request(method, path, body) {
31
46
  const url = `${this.baseUrl}${path}`;
32
47
  const headers = BaseClient.authHeaders(this.apiKey);
48
+ headers["x-sl-origin"] = this.origin;
33
49
  let response;
34
50
  try {
35
51
  response = await fetch(url, {
@@ -54,8 +70,10 @@ class BaseClient {
54
70
  }
55
71
  const errorBody = await response.text();
56
72
  let message = `HTTP ${response.status}`;
73
+ let parsedBody = errorBody;
57
74
  try {
58
75
  const json = JSON.parse(errorBody);
76
+ parsedBody = json;
59
77
  const err = json.error ?? json.message;
60
78
  if (typeof err === "string") {
61
79
  message = err;
@@ -66,7 +84,7 @@ class BaseClient {
66
84
  if (errorBody)
67
85
  message = errorBody;
68
86
  }
69
- throw new ApiError(response.status, message);
87
+ throw new ApiError(response.status, message, parsedBody);
70
88
  }
71
89
  if (response.status === 204) {
72
90
  return;
@@ -76,9 +94,116 @@ class BaseClient {
76
94
  }
77
95
 
78
96
  // src/workflows/client.ts
97
+ function parseSseChunk(raw) {
98
+ let event = "message";
99
+ const dataLines = [];
100
+ for (const line of raw.split(`
101
+ `)) {
102
+ if (line.startsWith("event:")) {
103
+ event = line.slice(6).trim();
104
+ } else if (line.startsWith("data:")) {
105
+ dataLines.push(line.slice(5).trimStart());
106
+ }
107
+ }
108
+ if (dataLines.length === 0)
109
+ return null;
110
+ const data = dataLines.join(`
111
+ `);
112
+ try {
113
+ const parsed = JSON.parse(data);
114
+ switch (event) {
115
+ case "step":
116
+ return { type: "step", step: parsed };
117
+ case "done":
118
+ return { type: "done", done: parsed };
119
+ case "heartbeat":
120
+ return {
121
+ type: "heartbeat",
122
+ ts: typeof parsed === "string" ? parsed : String(parsed)
123
+ };
124
+ case "timeout":
125
+ return {
126
+ type: "timeout",
127
+ message: parsed.message ?? "timeout"
128
+ };
129
+ default:
130
+ return null;
131
+ }
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
136
+
79
137
  class Workflows extends BaseClient {
80
138
  async deploy(data) {
81
- return this.request("POST", "/api/workflows", data);
139
+ try {
140
+ return await this.request("POST", "/api/workflows", data);
141
+ } catch (err) {
142
+ if (err instanceof ApiError && err.status === 409) {
143
+ const body = err.body;
144
+ if (body?.currentVersion && body.expectedVersion) {
145
+ throw new VersionConflictError(body.currentVersion, body.expectedVersion, err.message);
146
+ }
147
+ }
148
+ throw err;
149
+ }
150
+ }
151
+ async getSource(name) {
152
+ return this.request("GET", `/api/workflows/${name}/source`);
153
+ }
154
+ async bundle(data) {
155
+ return this.request("POST", "/api/workflows/bundle", data);
156
+ }
157
+ async pauseAll() {
158
+ return this.request("POST", "/api/workflows/pause-all");
159
+ }
160
+ async cancelRun(runId) {
161
+ return this.request("POST", `/api/workflows/runs/${runId}/cancel`);
162
+ }
163
+ async rollback(name, toVersion) {
164
+ return this.request("POST", `/api/workflows/${name}/rollback`, toVersion ? { toVersion } : {});
165
+ }
166
+ async streamRun(name, runId, onEvent, signal) {
167
+ const url = `${this.baseUrl}/api/workflows/${name}/runs/${runId}/stream`;
168
+ const headers = {
169
+ Accept: "text/event-stream",
170
+ "x-sl-origin": this.origin
171
+ };
172
+ if (this.apiKey) {
173
+ headers.Authorization = `Bearer ${this.apiKey}`;
174
+ }
175
+ const res = await fetch(url, { headers, signal });
176
+ if (!res.ok || !res.body) {
177
+ throw new ApiError(res.status, `Failed to open workflow run stream (HTTP ${res.status})`);
178
+ }
179
+ const reader = res.body.pipeThrough(new TextDecoderStream).getReader();
180
+ let buffer = "";
181
+ while (true) {
182
+ const { value, done } = await reader.read();
183
+ if (done)
184
+ break;
185
+ buffer += value;
186
+ let sep = buffer.indexOf(`
187
+
188
+ `);
189
+ while (sep !== -1) {
190
+ const chunk = buffer.slice(0, sep);
191
+ buffer = buffer.slice(sep + 2);
192
+ const parsed = parseSseChunk(chunk);
193
+ if (parsed) {
194
+ onEvent(parsed);
195
+ if (parsed.type === "done" || parsed.type === "timeout") {
196
+ await reader.cancel().catch(() => {
197
+ return;
198
+ });
199
+ return;
200
+ }
201
+ }
202
+ sep = buffer.indexOf(`
203
+
204
+ `);
205
+ }
206
+ }
82
207
  }
83
208
  async list() {
84
209
  return this.request("GET", "/api/workflows");
@@ -115,5 +240,5 @@ export {
115
240
  Workflows
116
241
  };
117
242
 
118
- //# debugId=18AC9997BF73F08564756E2164756E21
243
+ //# debugId=653D77D9FE60E3F464756E2164756E21
119
244
  //# sourceMappingURL=index.js.map
@@ -2,11 +2,11 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/errors.ts", "../src/base.ts", "../src/workflows/client.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Error thrown by {@link SecondLayer} when an API request fails.\n * Includes the HTTP status code for programmatic error handling.\n *\n * @example\n * ```ts\n * try {\n * await client.streams.get(\"abc123\");\n * } catch (err) {\n * if (err instanceof ApiError && err.status === 404) {\n * console.log(\"Stream not found\");\n * }\n * }\n * ```\n */\nexport class ApiError extends Error {\n\tconstructor(\n\t\t/** HTTP status code (0 for network errors). */\n\t\tpublic status: number,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t}\n}\n",
6
- "import { ApiError } from \"./errors.ts\";\n\nexport interface SecondLayerOptions {\n\t/** Base URL of the Secondlayer API (trailing slashes are stripped). */\n\tbaseUrl: string;\n\t/** Bearer token for authenticated requests. */\n\tapiKey?: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.secondlayer.tools\";\n\nexport abstract class BaseClient {\n\tprotected baseUrl: string;\n\tprotected apiKey?: string;\n\n\tconstructor(options: Partial<SecondLayerOptions> = {}) {\n\t\tthis.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\t\tthis.apiKey = options.apiKey;\n\t}\n\n\tstatic authHeaders(apiKey?: string): Record<string, string> {\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t};\n\t\tif (apiKey) {\n\t\t\theaders[\"Authorization\"] = `Bearer ${apiKey}`;\n\t\t}\n\t\treturn headers;\n\t}\n\n\tprotected async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst url = `${this.baseUrl}${path}`;\n\t\tconst headers = BaseClient.authHeaders(this.apiKey);\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\tmethod,\n\t\t\t\theaders,\n\t\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t\t});\n\t\t} catch {\n\t\t\tthrow new ApiError(\n\t\t\t\t0,\n\t\t\t\t`Cannot reach API at ${this.baseUrl}. Check your connection or try again.`,\n\t\t\t);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tif (response.status === 401) {\n\t\t\t\tthrow new ApiError(401, \"API key invalid or expired.\");\n\t\t\t}\n\n\t\t\tif (response.status === 429) {\n\t\t\t\tconst retryAfter = response.headers.get(\"Retry-After\");\n\t\t\t\tconst msg = retryAfter\n\t\t\t\t\t? `Rate limited. Wait ${retryAfter} seconds.`\n\t\t\t\t\t: \"Rate limited. Try again later.\";\n\t\t\t\tthrow new ApiError(429, msg);\n\t\t\t}\n\n\t\t\tif (response.status >= 500) {\n\t\t\t\tthrow new ApiError(\n\t\t\t\t\tresponse.status,\n\t\t\t\t\t`Server error. Try again or check status at ${this.baseUrl}/health`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst errorBody = await response.text();\n\t\t\tlet message = `HTTP ${response.status}`;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(errorBody);\n\t\t\t\tconst err = json.error ?? json.message;\n\t\t\t\tif (typeof err === \"string\") {\n\t\t\t\t\tmessage = err;\n\t\t\t\t} else if (err && typeof err === \"object\") {\n\t\t\t\t\tmessage = JSON.stringify(err);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tif (errorBody) message = errorBody;\n\t\t\t}\n\t\t\tthrow new ApiError(response.status, message);\n\t\t}\n\n\t\tif (response.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\n\t\treturn response.json() as Promise<T>;\n\t}\n}\n",
7
- "import type {\n\tWorkflowRun,\n\tWorkflowRunStatus,\n} from \"@secondlayer/workflows\";\nimport { BaseClient } from \"../base.ts\";\n\nexport interface WorkflowSummary {\n\tname: string;\n\tstatus: \"active\" | \"paused\";\n\ttriggerType: \"event\" | \"stream\" | \"schedule\" | \"manual\";\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface WorkflowDetail extends WorkflowSummary {\n\ttrigger: Record<string, unknown>;\n\tretries?: { maxAttempts?: number; backoffMs?: number };\n\ttimeout?: number;\n\ttotalRuns: number;\n\tlastRunAt: string | null;\n}\n\nexport interface WorkflowRunSummary {\n\tid: string;\n\tworkflowName: string;\n\tstatus: WorkflowRunStatus;\n\tduration: number;\n\taiTokensUsed: number;\n\ttriggeredAt: string;\n\tcompletedAt: string | null;\n}\n\nexport class Workflows extends BaseClient {\n\tasync deploy(data: {\n\t\tname: string;\n\t\ttrigger: Record<string, unknown>;\n\t\thandlerCode: string;\n\t\tretries?: Record<string, unknown>;\n\t\ttimeout?: number;\n\t}): Promise<{ action: string; workflowId: string; message: string }> {\n\t\treturn this.request(\"POST\", \"/api/workflows\", data);\n\t}\n\n\tasync list(): Promise<{ workflows: WorkflowSummary[] }> {\n\t\treturn this.request(\"GET\", \"/api/workflows\");\n\t}\n\n\tasync get(name: string): Promise<WorkflowDetail> {\n\t\treturn this.request(\"GET\", `/api/workflows/${name}`);\n\t}\n\n\tasync trigger(\n\t\tname: string,\n\t\tinput?: Record<string, unknown>,\n\t): Promise<{ runId: string }> {\n\t\treturn this.request(\"POST\", `/api/workflows/${name}/trigger`, input ? { input } : undefined);\n\t}\n\n\tasync pause(name: string): Promise<void> {\n\t\treturn this.request(\"POST\", `/api/workflows/${name}/pause`);\n\t}\n\n\tasync resume(name: string): Promise<void> {\n\t\treturn this.request(\"POST\", `/api/workflows/${name}/resume`);\n\t}\n\n\tasync delete(name: string): Promise<void> {\n\t\treturn this.request(\"DELETE\", `/api/workflows/${name}`);\n\t}\n\n\tasync listRuns(\n\t\tname: string,\n\t\tparams?: { status?: WorkflowRunStatus; limit?: number },\n\t): Promise<{ runs: WorkflowRunSummary[] }> {\n\t\tconst qs = new URLSearchParams();\n\t\tif (params?.status) qs.set(\"status\", params.status);\n\t\tif (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n\t\tconst query = qs.toString();\n\t\treturn this.request(\"GET\", `/api/workflows/${name}/runs${query ? `?${query}` : \"\"}`);\n\t}\n\n\tasync getRun(runId: string): Promise<WorkflowRun> {\n\t\treturn this.request(\"GET\", `/api/workflows/runs/${runId}`);\n\t}\n}\n"
5
+ "/**\n * Error thrown by {@link SecondLayer} when an API request fails.\n * Includes the HTTP status code for programmatic error handling.\n *\n * @example\n * ```ts\n * try {\n * await client.subgraphs.get(\"my-subgraph\");\n * } catch (err) {\n * if (err instanceof ApiError && err.status === 404) {\n * console.log(\"Subgraph not found\");\n * }\n * }\n * ```\n */\nexport class ApiError extends Error {\n\tconstructor(\n\t\t/** HTTP status code (0 for network errors). */\n\t\tpublic status: number,\n\t\tmessage: string,\n\t\t/** Raw response body (parsed JSON if possible) — preserved for callers that need error details. */\n\t\tpublic body?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t}\n}\n\n/**\n * Thrown by {@link Workflows.deploy} when the server rejects a deploy because the\n * provided `expectedVersion` does not match the current stored version.\n */\nexport class VersionConflictError extends ApiError {\n\tconstructor(\n\t\tpublic currentVersion: string,\n\t\tpublic expectedVersion: string,\n\t\tmessage = `Version conflict: expected ${expectedVersion}, current ${currentVersion}`,\n\t) {\n\t\tsuper(409, message, { currentVersion, expectedVersion });\n\t\tthis.name = \"VersionConflictError\";\n\t}\n}\n",
6
+ "import { ApiError } from \"./errors.ts\";\n\nexport interface SecondLayerOptions {\n\t/** Base URL of the Secondlayer API (trailing slashes are stripped). */\n\tbaseUrl: string;\n\t/** Bearer token for authenticated requests. */\n\tapiKey?: string;\n\t/** Deploy origin label sent as `x-sl-origin` (telemetry). Defaults to `cli`. */\n\torigin?: \"cli\" | \"mcp\" | \"session\";\n}\n\nconst DEFAULT_BASE_URL = \"https://api.secondlayer.tools\";\n\nexport abstract class BaseClient {\n\tprotected baseUrl: string;\n\tprotected apiKey?: string;\n\tprotected origin: \"cli\" | \"mcp\" | \"session\";\n\n\tconstructor(options: Partial<SecondLayerOptions> = {}) {\n\t\tthis.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.origin = options.origin ?? \"cli\";\n\t}\n\n\tstatic authHeaders(apiKey?: string): Record<string, string> {\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t};\n\t\tif (apiKey) {\n\t\t\theaders.Authorization = `Bearer ${apiKey}`;\n\t\t}\n\t\treturn headers;\n\t}\n\n\tprotected async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst url = `${this.baseUrl}${path}`;\n\t\tconst headers = BaseClient.authHeaders(this.apiKey);\n\t\theaders[\"x-sl-origin\"] = this.origin;\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\tmethod,\n\t\t\t\theaders,\n\t\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t\t});\n\t\t} catch {\n\t\t\tthrow new ApiError(\n\t\t\t\t0,\n\t\t\t\t`Cannot reach API at ${this.baseUrl}. Check your connection or try again.`,\n\t\t\t);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tif (response.status === 401) {\n\t\t\t\tthrow new ApiError(401, \"API key invalid or expired.\");\n\t\t\t}\n\n\t\t\tif (response.status === 429) {\n\t\t\t\tconst retryAfter = response.headers.get(\"Retry-After\");\n\t\t\t\tconst msg = retryAfter\n\t\t\t\t\t? `Rate limited. Wait ${retryAfter} seconds.`\n\t\t\t\t\t: \"Rate limited. Try again later.\";\n\t\t\t\tthrow new ApiError(429, msg);\n\t\t\t}\n\n\t\t\tif (response.status >= 500) {\n\t\t\t\tthrow new ApiError(\n\t\t\t\t\tresponse.status,\n\t\t\t\t\t`Server error. Try again or check status at ${this.baseUrl}/health`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst errorBody = await response.text();\n\t\t\tlet message = `HTTP ${response.status}`;\n\t\t\tlet parsedBody: unknown = errorBody;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(errorBody);\n\t\t\t\tparsedBody = json;\n\t\t\t\tconst err = json.error ?? json.message;\n\t\t\t\tif (typeof err === \"string\") {\n\t\t\t\t\tmessage = err;\n\t\t\t\t} else if (err && typeof err === \"object\") {\n\t\t\t\t\tmessage = JSON.stringify(err);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tif (errorBody) message = errorBody;\n\t\t\t}\n\t\t\tthrow new ApiError(response.status, message, parsedBody);\n\t\t}\n\n\t\tif (response.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\n\t\treturn response.json() as Promise<T>;\n\t}\n}\n",
7
+ "import type { WorkflowRun, WorkflowRunStatus } from \"@secondlayer/workflows\";\nimport { BaseClient } from \"../base.ts\";\nimport { ApiError, VersionConflictError } from \"../errors.ts\";\n\nfunction parseSseChunk(raw: string): WorkflowTailEvent | null {\n\tlet event = \"message\";\n\tconst dataLines: string[] = [];\n\tfor (const line of raw.split(\"\\n\")) {\n\t\tif (line.startsWith(\"event:\")) {\n\t\t\tevent = line.slice(6).trim();\n\t\t} else if (line.startsWith(\"data:\")) {\n\t\t\tdataLines.push(line.slice(5).trimStart());\n\t\t}\n\t}\n\tif (dataLines.length === 0) return null;\n\tconst data = dataLines.join(\"\\n\");\n\ttry {\n\t\tconst parsed = JSON.parse(data);\n\t\tswitch (event) {\n\t\t\tcase \"step\":\n\t\t\t\treturn { type: \"step\", step: parsed as WorkflowStepEvent };\n\t\t\tcase \"done\":\n\t\t\t\treturn { type: \"done\", done: parsed as WorkflowRunDoneEvent };\n\t\t\tcase \"heartbeat\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"heartbeat\",\n\t\t\t\t\tts: typeof parsed === \"string\" ? parsed : String(parsed),\n\t\t\t\t};\n\t\t\tcase \"timeout\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"timeout\",\n\t\t\t\t\tmessage: (parsed as { message?: string }).message ?? \"timeout\",\n\t\t\t\t};\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport interface WorkflowSource {\n\tname: string;\n\tversion: string;\n\tsourceCode: string | null;\n\treadOnly: boolean;\n\treason?: string;\n\tupdatedAt: string;\n}\n\nexport interface WorkflowStepEvent {\n\tid: string;\n\tstepIndex: number;\n\tstepId: string;\n\tstepType: string;\n\tstatus: string;\n\toutput?: unknown;\n\terror: string | null;\n\tretryCount: number;\n\taiTokensUsed: number;\n\tstartedAt: string | null;\n\tcompletedAt: string | null;\n\tdurationMs: number | null;\n\tts: string;\n}\n\nexport interface WorkflowRunDoneEvent {\n\trunId: string;\n\tstatus: string;\n\terror?: string | null;\n\tcompletedAt?: string | null;\n}\n\nexport type WorkflowTailEvent =\n\t| { type: \"step\"; step: WorkflowStepEvent }\n\t| { type: \"done\"; done: WorkflowRunDoneEvent }\n\t| { type: \"heartbeat\"; ts: string }\n\t| { type: \"timeout\"; message: string };\n\nexport interface DeployDryRunResponse {\n\tvalid: boolean;\n\tvalidation?: { name: string; triggerType: string };\n\tbundleSize: number;\n\terror?: string;\n}\n\nexport interface DeployResponse {\n\taction: \"created\" | \"updated\";\n\tworkflowId: string;\n\tversion: string;\n\tmessage: string;\n}\n\nexport interface BundleWorkflowResponse {\n\tok: true;\n\tname: string;\n\ttrigger: Record<string, unknown>;\n\thandlerCode: string;\n\tsourceCode: string;\n\tretries: Record<string, unknown> | null;\n\ttimeout: number | null;\n\tbundleSize: number;\n}\n\nexport interface WorkflowSummary {\n\tname: string;\n\tstatus: \"active\" | \"paused\";\n\ttriggerType: \"event\" | \"schedule\" | \"manual\";\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface WorkflowDetail extends WorkflowSummary {\n\ttrigger: Record<string, unknown>;\n\tretries?: { maxAttempts?: number; backoffMs?: number };\n\ttimeout?: number;\n\ttotalRuns: number;\n\tlastRunAt: string | null;\n}\n\nexport interface WorkflowRunSummary {\n\tid: string;\n\tworkflowName: string;\n\tstatus: WorkflowRunStatus;\n\tduration: number;\n\taiTokensUsed: number;\n\ttriggeredAt: string;\n\tcompletedAt: string | null;\n}\n\nexport class Workflows extends BaseClient {\n\tasync deploy(\n\t\tdata: {\n\t\t\tname: string;\n\t\t\ttrigger: Record<string, unknown>;\n\t\t\thandlerCode: string;\n\t\t\tsourceCode?: string;\n\t\t\texpectedVersion?: string;\n\t\t\tretries?: Record<string, unknown>;\n\t\t\ttimeout?: number;\n\t\t\tclientRequestId?: string;\n\t\t} & { dryRun?: false },\n\t): Promise<DeployResponse>;\n\tasync deploy(data: {\n\t\tname: string;\n\t\ttrigger: Record<string, unknown>;\n\t\thandlerCode: string;\n\t\tsourceCode?: string;\n\t\texpectedVersion?: string;\n\t\tretries?: Record<string, unknown>;\n\t\ttimeout?: number;\n\t\tdryRun: true;\n\t}): Promise<DeployDryRunResponse>;\n\tasync deploy(data: {\n\t\tname: string;\n\t\ttrigger: Record<string, unknown>;\n\t\thandlerCode: string;\n\t\tsourceCode?: string;\n\t\texpectedVersion?: string;\n\t\tdryRun?: boolean;\n\t\tclientRequestId?: string;\n\t\tretries?: Record<string, unknown>;\n\t\ttimeout?: number;\n\t}): Promise<DeployResponse | DeployDryRunResponse> {\n\t\ttry {\n\t\t\treturn await this.request(\"POST\", \"/api/workflows\", data);\n\t\t} catch (err) {\n\t\t\tif (err instanceof ApiError && err.status === 409) {\n\t\t\t\tconst body = err.body as\n\t\t\t\t\t| { currentVersion?: string; expectedVersion?: string }\n\t\t\t\t\t| undefined;\n\t\t\t\tif (body?.currentVersion && body.expectedVersion) {\n\t\t\t\t\tthrow new VersionConflictError(\n\t\t\t\t\t\tbody.currentVersion,\n\t\t\t\t\t\tbody.expectedVersion,\n\t\t\t\t\t\terr.message,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync getSource(name: string): Promise<WorkflowSource> {\n\t\treturn this.request(\"GET\", `/api/workflows/${name}/source`);\n\t}\n\n\t/**\n\t * Bundle a TypeScript workflow source on the server. Used by the web chat\n\t * authoring loop so Vercel's serverless runtime doesn't have to run esbuild.\n\t * CLI and MCP still bundle locally — this method is for clients that can't\n\t * install `@secondlayer/bundler` directly (e.g. browser tooling, edge\n\t * functions).\n\t */\n\tasync bundle(data: { code: string }): Promise<BundleWorkflowResponse> {\n\t\treturn this.request(\"POST\", \"/api/workflows/bundle\", data);\n\t}\n\n\tasync pauseAll(): Promise<{\n\t\tpaused: number;\n\t\tworkflows: Array<{ name: string; version: string; status: string }>;\n\t}> {\n\t\treturn this.request(\"POST\", \"/api/workflows/pause-all\");\n\t}\n\n\tasync cancelRun(runId: string): Promise<{\n\t\trunId: string;\n\t\tstatus: string;\n\t\tcancelled: boolean;\n\t\tcompletedAt?: string;\n\t\tmessage?: string;\n\t}> {\n\t\treturn this.request(\"POST\", `/api/workflows/runs/${runId}/cancel`);\n\t}\n\n\tasync rollback(\n\t\tname: string,\n\t\ttoVersion?: string,\n\t): Promise<{\n\t\taction: \"rolled-back\";\n\t\tname: string;\n\t\tfromVersion: string;\n\t\trestoredFromVersion: string;\n\t\tversion: string;\n\t}> {\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/api/workflows/${name}/rollback`,\n\t\t\ttoVersion ? { toVersion } : {},\n\t\t);\n\t}\n\n\t/**\n\t * Subscribe to a workflow run's server-sent event stream. Resolves when the\n\t * run completes, times out, or the signal is aborted. Throws on HTTP errors\n\t * opening the stream.\n\t */\n\tasync streamRun(\n\t\tname: string,\n\t\trunId: string,\n\t\tonEvent: (event: WorkflowTailEvent) => void,\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tconst url = `${this.baseUrl}/api/workflows/${name}/runs/${runId}/stream`;\n\t\tconst headers: Record<string, string> = {\n\t\t\tAccept: \"text/event-stream\",\n\t\t\t\"x-sl-origin\": this.origin,\n\t\t};\n\t\tif (this.apiKey) {\n\t\t\theaders.Authorization = `Bearer ${this.apiKey}`;\n\t\t}\n\n\t\tconst res = await fetch(url, { headers, signal });\n\t\tif (!res.ok || !res.body) {\n\t\t\tthrow new ApiError(\n\t\t\t\tres.status,\n\t\t\t\t`Failed to open workflow run stream (HTTP ${res.status})`,\n\t\t\t);\n\t\t}\n\n\t\tconst reader = res.body.pipeThrough(new TextDecoderStream()).getReader();\n\t\tlet buffer = \"\";\n\n\t\twhile (true) {\n\t\t\tconst { value, done } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tbuffer += value;\n\n\t\t\tlet sep = buffer.indexOf(\"\\n\\n\");\n\t\t\twhile (sep !== -1) {\n\t\t\t\tconst chunk = buffer.slice(0, sep);\n\t\t\t\tbuffer = buffer.slice(sep + 2);\n\t\t\t\tconst parsed = parseSseChunk(chunk);\n\t\t\t\tif (parsed) {\n\t\t\t\t\tonEvent(parsed);\n\t\t\t\t\tif (parsed.type === \"done\" || parsed.type === \"timeout\") {\n\t\t\t\t\t\tawait reader.cancel().catch(() => undefined);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsep = buffer.indexOf(\"\\n\\n\");\n\t\t\t}\n\t\t}\n\t}\n\n\tasync list(): Promise<{ workflows: WorkflowSummary[] }> {\n\t\treturn this.request(\"GET\", \"/api/workflows\");\n\t}\n\n\tasync get(name: string): Promise<WorkflowDetail> {\n\t\treturn this.request(\"GET\", `/api/workflows/${name}`);\n\t}\n\n\tasync trigger(\n\t\tname: string,\n\t\tinput?: Record<string, unknown>,\n\t): Promise<{ runId: string }> {\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/api/workflows/${name}/trigger`,\n\t\t\tinput ? { input } : undefined,\n\t\t);\n\t}\n\n\tasync pause(name: string): Promise<void> {\n\t\treturn this.request(\"POST\", `/api/workflows/${name}/pause`);\n\t}\n\n\tasync resume(name: string): Promise<void> {\n\t\treturn this.request(\"POST\", `/api/workflows/${name}/resume`);\n\t}\n\n\tasync delete(name: string): Promise<void> {\n\t\treturn this.request(\"DELETE\", `/api/workflows/${name}`);\n\t}\n\n\tasync listRuns(\n\t\tname: string,\n\t\tparams?: { status?: WorkflowRunStatus; limit?: number },\n\t): Promise<{ runs: WorkflowRunSummary[] }> {\n\t\tconst qs = new URLSearchParams();\n\t\tif (params?.status) qs.set(\"status\", params.status);\n\t\tif (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n\t\tconst query = qs.toString();\n\t\treturn this.request(\n\t\t\t\"GET\",\n\t\t\t`/api/workflows/${name}/runs${query ? `?${query}` : \"\"}`,\n\t\t);\n\t}\n\n\tasync getRun(runId: string): Promise<WorkflowRun> {\n\t\treturn this.request(\"GET\", `/api/workflows/runs/${runId}`);\n\t}\n}\n"
8
8
  ],
9
- "mappings": "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",
10
- "debugId": "18AC9997BF73F08564756E2164756E21",
9
+ "mappings": "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",
10
+ "debugId": "653D77D9FE60E3F464756E2164756E21",
11
11
  "names": []
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/sdk",
3
- "version": "0.10.2",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -9,10 +9,6 @@
9
9
  "types": "./dist/index.d.ts",
10
10
  "import": "./dist/index.js"
11
11
  },
12
- "./streams": {
13
- "types": "./dist/streams/index.d.ts",
14
- "import": "./dist/streams/index.js"
15
- },
16
12
  "./subgraphs": {
17
13
  "types": "./dist/subgraphs/index.d.ts",
18
14
  "import": "./dist/subgraphs/index.js"
@@ -37,9 +33,9 @@
37
33
  "prepublishOnly": "bun run build"
38
34
  },
39
35
  "dependencies": {
40
- "@secondlayer/shared": "^0.12.0",
41
- "@secondlayer/subgraphs": "^0.11.0",
42
- "@secondlayer/workflows": "^0.0.3"
36
+ "@secondlayer/shared": "^1.0.0",
37
+ "@secondlayer/subgraphs": "^0.11.6",
38
+ "@secondlayer/workflows": "^1.0.0"
43
39
  },
44
40
  "devDependencies": {
45
41
  "@types/bun": "latest",
@@ -1,57 +0,0 @@
1
- import { BulkPauseResponse, BulkResumeResponse, CreateStream, CreateStreamResponse, ListStreamsResponse, StreamResponse, UpdateStream } from "@secondlayer/shared/schemas";
2
- interface SecondLayerOptions {
3
- /** Base URL of the Secondlayer API (trailing slashes are stripped). */
4
- baseUrl: string;
5
- /** Bearer token for authenticated requests. */
6
- apiKey?: string;
7
- }
8
- declare abstract class BaseClient {
9
- protected baseUrl: string;
10
- protected apiKey?: string;
11
- constructor(options?: Partial<SecondLayerOptions>);
12
- static authHeaders(apiKey?: string): Record<string, string>;
13
- protected request<T>(method: string, path: string, body?: unknown): Promise<T>;
14
- }
15
- interface DeliverySummary {
16
- id: string;
17
- blockHeight: number;
18
- status: string;
19
- statusCode: number | null;
20
- responseTimeMs: number | null;
21
- attempts: number;
22
- error: string | null;
23
- createdAt: string;
24
- }
25
- interface DeliveryDetail extends DeliverySummary {
26
- payload: unknown;
27
- }
28
- interface DeliveriesResponse {
29
- deliveries: DeliverySummary[];
30
- }
31
- declare class Streams extends BaseClient {
32
- private requestWithStreamId;
33
- resolveStreamId(partialId: string): Promise<string>;
34
- create(data: CreateStream): Promise<CreateStreamResponse>;
35
- update(id: string, data: UpdateStream): Promise<StreamResponse>;
36
- updateByName(name: string, data: CreateStream): Promise<StreamResponse>;
37
- list(params?: {
38
- status?: string
39
- }): Promise<ListStreamsResponse>;
40
- get(id: string): Promise<StreamResponse>;
41
- delete(id: string): Promise<void>;
42
- enable(id: string): Promise<StreamResponse>;
43
- disable(id: string): Promise<StreamResponse>;
44
- rotateSecret(id: string): Promise<{
45
- secret: string
46
- }>;
47
- /** List recent deliveries for a stream. */
48
- listDeliveries(id: string, params?: {
49
- limit?: number
50
- status?: string
51
- }): Promise<DeliveriesResponse>;
52
- /** Get a single delivery with full payload. */
53
- getDelivery(streamId: string, deliveryId: string): Promise<DeliveryDetail>;
54
- pauseAll(): Promise<BulkPauseResponse>;
55
- resumeAll(): Promise<BulkResumeResponse>;
56
- }
57
- export { Streams, SecondLayerOptions };
@@ -1,160 +0,0 @@
1
- // src/errors.ts
2
- class ApiError extends Error {
3
- status;
4
- constructor(status, message) {
5
- super(message);
6
- this.status = status;
7
- this.name = "ApiError";
8
- }
9
- }
10
-
11
- // src/base.ts
12
- var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
13
-
14
- class BaseClient {
15
- baseUrl;
16
- apiKey;
17
- constructor(options = {}) {
18
- this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
19
- this.apiKey = options.apiKey;
20
- }
21
- static authHeaders(apiKey) {
22
- const headers = {
23
- "Content-Type": "application/json"
24
- };
25
- if (apiKey) {
26
- headers["Authorization"] = `Bearer ${apiKey}`;
27
- }
28
- return headers;
29
- }
30
- async request(method, path, body) {
31
- const url = `${this.baseUrl}${path}`;
32
- const headers = BaseClient.authHeaders(this.apiKey);
33
- let response;
34
- try {
35
- response = await fetch(url, {
36
- method,
37
- headers,
38
- body: body ? JSON.stringify(body) : undefined
39
- });
40
- } catch {
41
- throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);
42
- }
43
- if (!response.ok) {
44
- if (response.status === 401) {
45
- throw new ApiError(401, "API key invalid or expired.");
46
- }
47
- if (response.status === 429) {
48
- const retryAfter = response.headers.get("Retry-After");
49
- const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
50
- throw new ApiError(429, msg);
51
- }
52
- if (response.status >= 500) {
53
- throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
54
- }
55
- const errorBody = await response.text();
56
- let message = `HTTP ${response.status}`;
57
- try {
58
- const json = JSON.parse(errorBody);
59
- const err = json.error ?? json.message;
60
- if (typeof err === "string") {
61
- message = err;
62
- } else if (err && typeof err === "object") {
63
- message = JSON.stringify(err);
64
- }
65
- } catch {
66
- if (errorBody)
67
- message = errorBody;
68
- }
69
- throw new ApiError(response.status, message);
70
- }
71
- if (response.status === 204) {
72
- return;
73
- }
74
- return response.json();
75
- }
76
- }
77
-
78
- // src/streams/client.ts
79
- class Streams extends BaseClient {
80
- async requestWithStreamId(method, pathTemplate, id, body) {
81
- const fullId = await this.resolveStreamId(id);
82
- return this.request(method, pathTemplate(fullId), body);
83
- }
84
- async resolveStreamId(partialId) {
85
- if (partialId.length === 36 && partialId.includes("-")) {
86
- return partialId;
87
- }
88
- const { streams } = await this.list();
89
- const matches = streams.filter((s) => s.id.startsWith(partialId));
90
- if (matches.length === 0) {
91
- throw new ApiError(404, `No stream found matching "${partialId}"`);
92
- }
93
- if (matches.length > 1) {
94
- throw new ApiError(400, `Multiple streams match "${partialId}": ${matches.map((s) => s.id.slice(0, 8)).join(", ")}`);
95
- }
96
- return matches[0].id;
97
- }
98
- async create(data) {
99
- return this.request("POST", "/api/streams", data);
100
- }
101
- async update(id, data) {
102
- return this.requestWithStreamId("PATCH", (id2) => `/api/streams/${id2}`, id, data);
103
- }
104
- async updateByName(name, data) {
105
- const { streams } = await this.list();
106
- const existing = streams.find((s) => s.name === name);
107
- if (!existing) {
108
- throw new ApiError(404, `Stream with name "${name}" not found`);
109
- }
110
- return this.update(existing.id, data);
111
- }
112
- async list(params) {
113
- const searchParams = new URLSearchParams;
114
- if (params?.status)
115
- searchParams.set("status", params.status);
116
- const query = searchParams.toString();
117
- const path = query ? `/api/streams?${query}` : "/api/streams";
118
- return this.request("GET", path);
119
- }
120
- async get(id) {
121
- return this.requestWithStreamId("GET", (id2) => `/api/streams/${id2}`, id);
122
- }
123
- async delete(id) {
124
- return this.requestWithStreamId("DELETE", (id2) => `/api/streams/${id2}`, id);
125
- }
126
- async enable(id) {
127
- return this.requestWithStreamId("POST", (id2) => `/api/streams/${id2}/enable`, id);
128
- }
129
- async disable(id) {
130
- return this.requestWithStreamId("POST", (id2) => `/api/streams/${id2}/disable`, id);
131
- }
132
- async rotateSecret(id) {
133
- return this.requestWithStreamId("POST", (id2) => `/api/streams/${id2}/rotate-secret`, id);
134
- }
135
- async listDeliveries(id, params) {
136
- const qs = new URLSearchParams;
137
- if (params?.limit !== undefined)
138
- qs.set("limit", String(params.limit));
139
- if (params?.status)
140
- qs.set("status", params.status);
141
- const query = qs.toString();
142
- return this.requestWithStreamId("GET", (id2) => `/api/streams/${id2}/deliveries${query ? `?${query}` : ""}`, id);
143
- }
144
- async getDelivery(streamId, deliveryId) {
145
- const fullId = await this.resolveStreamId(streamId);
146
- return this.request("GET", `/api/streams/${fullId}/deliveries/${deliveryId}`);
147
- }
148
- async pauseAll() {
149
- return this.request("POST", "/api/streams/pause");
150
- }
151
- async resumeAll() {
152
- return this.request("POST", "/api/streams/resume");
153
- }
154
- }
155
- export {
156
- Streams
157
- };
158
-
159
- //# debugId=C6255FAFEE54D50164756E2164756E21
160
- //# sourceMappingURL=index.js.map