@shardflux/sdk 0.5.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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * This file was auto-generated by openapi-typescript.
3
+ * Do not make direct changes to the file.
4
+ */
5
+ export {};
package/dist/http.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ export declare const SDK_VERSION = "0.5.0";
2
+ export interface RequestOptions {
3
+ query?: Record<string, string | number | boolean | undefined | null>;
4
+ json?: unknown;
5
+ body?: Uint8Array | string;
6
+ contentType?: string;
7
+ accept?: string;
8
+ headers?: Record<string, string>;
9
+ idempotencyKey?: string;
10
+ signal?: AbortSignal;
11
+ /** Override the client's default request timeout (ms); 0 disables it (streams). */
12
+ timeoutMs?: number;
13
+ }
14
+ export interface HttpOptions {
15
+ baseUrl: string;
16
+ fetch: typeof fetch;
17
+ userAgent: string;
18
+ timeoutMs: number;
19
+ maxRetries: number;
20
+ source: 'api' | 'cell';
21
+ sleep?: (ms: number) => Promise<void>;
22
+ }
23
+ export declare const defaultSleep: (ms: number) => Promise<void>;
24
+ export declare function buildUrl(base: string, path: string, query?: RequestOptions['query']): string;
25
+ /** Turns a non-2xx response into a ShardfluxApiError (or a protocol error for undocumented bodies). */
26
+ export declare function errorFrom(res: Response, source: 'api' | 'cell'): Promise<Error>;
27
+ export declare class HttpClient {
28
+ readonly opts: HttpOptions;
29
+ constructor(opts: HttpOptions);
30
+ /** Performs the request and returns the raw Response (2xx), throwing structured errors otherwise. */
31
+ raw(method: string, path: string, init?: RequestOptions, authorization?: string): Promise<Response>;
32
+ json<T>(method: string, path: string, init?: RequestOptions, authorization?: string): Promise<T>;
33
+ /** Like json() but also returns the HTTP status (open() distinguishes 200 from 202). */
34
+ jsonWithStatus<T>(method: string, path: string, init?: RequestOptions, authorization?: string): Promise<{
35
+ status: number;
36
+ body: T;
37
+ }>;
38
+ }
39
+ export declare function randomId(prefix?: string): string;
package/dist/http.js ADDED
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Minimal HTTP layer shared by the application API client and the cell
3
+ * client: JSON in/out, structured errors, per-request timeout, and bounded
4
+ * retries only where a retry cannot duplicate an effect (safe methods, or
5
+ * requests carrying an Idempotency-Key).
6
+ */
7
+ import { ShardfluxApiError, ShardfluxProtocolError, isErrorBody } from "./errors.js";
8
+ export const SDK_VERSION = '0.5.0';
9
+ export const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
10
+ export function buildUrl(base, path, query) {
11
+ const u = new URL(base.replace(/\/+$/, '') + path);
12
+ for (const [k, v] of Object.entries(query ?? {})) {
13
+ if (v !== undefined && v !== null)
14
+ u.searchParams.set(k, String(v));
15
+ }
16
+ return u.toString();
17
+ }
18
+ function retryAfterSeconds(res) {
19
+ const v = res.headers.get('retry-after');
20
+ if (v === null)
21
+ return undefined;
22
+ const n = Number(v);
23
+ return Number.isFinite(n) && n >= 0 ? n : undefined;
24
+ }
25
+ /** Turns a non-2xx response into a ShardfluxApiError (or a protocol error for undocumented bodies). */
26
+ export async function errorFrom(res, source) {
27
+ const text = await res.text();
28
+ let parsed;
29
+ try {
30
+ parsed = text.length > 0 ? JSON.parse(text) : null;
31
+ }
32
+ catch {
33
+ parsed = null;
34
+ }
35
+ if (isErrorBody(parsed))
36
+ return new ShardfluxApiError(res.status, parsed, source, retryAfterSeconds(res));
37
+ return new ShardfluxProtocolError(`HTTP ${res.status} from ${source === 'api' ? 'the Shardflux API' : 'the cell gateway'} without an error body`, res.status);
38
+ }
39
+ export class HttpClient {
40
+ opts;
41
+ constructor(opts) {
42
+ this.opts = opts;
43
+ }
44
+ /** Performs the request and returns the raw Response (2xx), throwing structured errors otherwise. */
45
+ async raw(method, path, init = {}, authorization) {
46
+ const url = buildUrl(this.opts.baseUrl, path, init.query);
47
+ const safe = method === 'GET' || method === 'HEAD';
48
+ const retriable = safe || init.idempotencyKey !== undefined;
49
+ const sleep = this.opts.sleep ?? defaultSleep;
50
+ for (let attempt = 0;; attempt += 1) {
51
+ const headers = {
52
+ accept: init.accept ?? 'application/json',
53
+ 'user-agent': this.opts.userAgent,
54
+ ...(authorization ? { authorization } : {}),
55
+ ...(init.idempotencyKey !== undefined ? { 'idempotency-key': init.idempotencyKey } : {}),
56
+ ...init.headers,
57
+ };
58
+ let body;
59
+ if (init.json !== undefined) {
60
+ body = JSON.stringify(init.json);
61
+ headers['content-type'] = 'application/json';
62
+ }
63
+ else if (init.body !== undefined) {
64
+ body = init.body;
65
+ headers['content-type'] = init.contentType ?? 'application/octet-stream';
66
+ }
67
+ const timeoutMs = init.timeoutMs ?? this.opts.timeoutMs;
68
+ const timeout = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
69
+ const signal = timeout && init.signal ? AbortSignal.any([timeout, init.signal]) : (timeout ?? init.signal);
70
+ let res;
71
+ try {
72
+ res = await this.opts.fetch(url, { method, headers, ...(body === undefined ? {} : { body: body }), ...(signal ? { signal } : {}) });
73
+ }
74
+ catch (err) {
75
+ if (init.signal?.aborted)
76
+ throw err;
77
+ if (!retriable || attempt >= this.opts.maxRetries)
78
+ throw err;
79
+ await sleep(Math.min(2_000, 200 * 2 ** attempt));
80
+ continue;
81
+ }
82
+ if (res.ok)
83
+ return res;
84
+ const error = await errorFrom(res, this.opts.source);
85
+ const retryableStatus = res.status === 429 || res.status === 502 || res.status === 503 || res.status === 504;
86
+ const retryableError = error instanceof ShardfluxApiError ? error.retryable && retryableStatus : retryableStatus;
87
+ if (!retriable || !retryableError || attempt >= this.opts.maxRetries)
88
+ throw error;
89
+ const hinted = error instanceof ShardfluxApiError ? error.retryAfterSeconds : undefined;
90
+ await sleep(Math.min(5_000, hinted !== undefined ? hinted * 1000 : 200 * 2 ** attempt));
91
+ }
92
+ }
93
+ async json(method, path, init = {}, authorization) {
94
+ const res = await this.raw(method, path, init, authorization);
95
+ if (res.status === 204)
96
+ return undefined;
97
+ const text = await res.text();
98
+ if (text.length === 0)
99
+ return undefined;
100
+ try {
101
+ return JSON.parse(text);
102
+ }
103
+ catch {
104
+ throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status);
105
+ }
106
+ }
107
+ /** Like json() but also returns the HTTP status (open() distinguishes 200 from 202). */
108
+ async jsonWithStatus(method, path, init = {}, authorization) {
109
+ const res = await this.raw(method, path, init, authorization);
110
+ const text = await res.text();
111
+ try {
112
+ return { status: res.status, body: (text.length === 0 ? undefined : JSON.parse(text)) };
113
+ }
114
+ catch {
115
+ throw new ShardfluxProtocolError(`${method} ${path}: response is not JSON`, res.status);
116
+ }
117
+ }
118
+ }
119
+ export function randomId(prefix = '') {
120
+ return `${prefix}${crypto.randomUUID()}`;
121
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @shardflux/sdk — TypeScript SDK for Shardflux persistent agent workspaces.
3
+ *
4
+ * import { Shardflux, workspaceTools } from '@shardflux/sdk';
5
+ * const cloud = new Shardflux({ apiKey: process.env.SHARDFLUX_API_KEY! });
6
+ * const workspace = await cloud.workspaces.open({ key: `${customerId}/${projectId}`, template: 'python-node-browser' });
7
+ * await agent.run({ input: userMessage, tools: workspaceTools(workspace) });
8
+ *
9
+ * Types come from the committed OpenAPI documents: application API
10
+ * and cell gateway.
11
+ */
12
+ export type { components, operations, paths } from './generated/app-api.js';
13
+ export type { components as CellComponents, paths as CellPaths } from './generated/cell-api.js';
14
+ export { BillingApi, Shardflux, WorkspacesApi, fetchBillingCatalog } from './client.js';
15
+ export type { AgentSession, BillingCatalog, BillingSubscription, Caps, CheckoutSession, Entitlements, Invoice, InvoicePage, ListParams, Me, OpenParams, OpenResponse, Operation, Page, PortalSession, ShardfluxOptions, WaitOptions, WorkspaceView, } from './client.js';
16
+ export { UsageApi } from './usage.js';
17
+ export type { Grants, Spend, SpendPolicy, UsageEstimate, UsageMeter, UsageSeries, UsageSeriesParams, UsageSummary } from './usage.js';
18
+ export { TemplateBuildTimeoutError, TemplateBuildsApi, TemplatesApi, buildSettled } from './templates.js';
19
+ export type { BuilderAvailability, CreateTemplateBuildParams, TemplateBuild, TemplateBuildLogUrl, TemplateBuildRegistrationState, TemplateBuildState, TemplateDetail, TemplateOwner, TemplateRecipe, TemplateSummary, TemplateVersion, TemplateVersionState, WaitForBuildOptions, } from './templates.js';
20
+ export { SecretsApi } from './secrets.js';
21
+ export type { CreateSecretParams, Secret, SecretPermissions, SecretScope, SecretVersion, UpdateSecretParams } from './secrets.js';
22
+ export { EgressPolicyApi } from './egress.js';
23
+ export type { EffectiveEgressPolicy, EgressEnforcement, EgressMode, EgressPolicyInput, EgressPolicyVersion, EgressRule, EgressRuleInput, OrganizationEgressOverride, ProjectEgressPolicy, WorkspaceEgressPolicy, } from './egress.js';
24
+ export { VolumesApi } from './volumes.js';
25
+ export type { AttachVolumeParams, AttachmentResult, CreateVolumeParams, DeleteVolumeParams, Volume, VolumeAttachment, VolumeAttachmentState, VolumeResult, VolumeState } from './volumes.js';
26
+ export { AuditApi, auditQuery, parseAuditNdjson } from './audit.js';
27
+ export type { AuditActorType, AuditEvent, AuditFilters, AuditListParams, AuditOutcome, AuditSource } from './audit.js';
28
+ export { Workspace } from './workspace.js';
29
+ export { CellClient, cellPath, ndjson } from './cell.js';
30
+ export type { BrowserContent, BrowserContentRequest, BrowserScreenshotRequest, CellClientOptions, ExecSession, ExecStartRequest, FileInfo, FileList, FileWriteResult, GitResult, GitStatus, OutputEvent, ProcessList, PtyOpenRequest, PtySession, RunOptions, RunResult, Signal, } from './cell.js';
31
+ export { ToolTokenManager } from './tokens.js';
32
+ export type { ToolName, ToolToken, ToolTokenOptions } from './tokens.js';
33
+ export { ToolArgumentError, executeToolCall, toAnthropicTools, toOpenAITools, validateArgs, workspaceTools } from './tools.js';
34
+ export type { JsonSchema, WorkspaceTool, WorkspaceToolsOptions } from './tools.js';
35
+ export { OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError } from './errors.js';
36
+ export type { AppErrorCode, CellErrorCode, ErrorCode } from './errors.js';
37
+ export { SDK_VERSION } from './http.js';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export { BillingApi, Shardflux, WorkspacesApi, fetchBillingCatalog } from "./client.js";
2
+ export { UsageApi } from "./usage.js";
3
+ export { TemplateBuildTimeoutError, TemplateBuildsApi, TemplatesApi, buildSettled } from "./templates.js";
4
+ export { SecretsApi } from "./secrets.js";
5
+ export { EgressPolicyApi } from "./egress.js";
6
+ export { VolumesApi } from "./volumes.js";
7
+ export { AuditApi, auditQuery, parseAuditNdjson } from "./audit.js";
8
+ export { Workspace } from "./workspace.js";
9
+ export { CellClient, cellPath, ndjson } from "./cell.js";
10
+ export { ToolTokenManager } from "./tokens.js";
11
+ export { ToolArgumentError, executeToolCall, toAnthropicTools, toOpenAITools, validateArgs, workspaceTools } from "./tools.js";
12
+ export { OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError } from "./errors.js";
13
+ export { SDK_VERSION } from "./http.js";
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Customer secrets over the application API (/v1). Types are written by hand.
3
+ *
4
+ * A project API key manages the secrets of its own project. Values are
5
+ * write-only: no method here returns one. The cell gateway obtains values at
6
+ * exec/PTY session start through POST /v1/internal/secret-resolutions with the
7
+ * caller's tool token; that endpoint is not part of the customer SDK.
8
+ * Organization-wide secrets and access logs are managed by owners/admins in the
9
+ * browser (/api/v1).
10
+ */
11
+ import type { ClientContext, Page } from './client.js';
12
+ import type { ToolName } from './tokens.js';
13
+ export type SecretScope = 'organization' | 'project';
14
+ export interface SecretPermissions {
15
+ /** Organization secrets only: null = every project, [] = none. Always null for project secrets. */
16
+ allowed_project_ids: string[] | null;
17
+ /** null = any workspace of the allowed projects. Forks do not inherit workspace-scoped permissions. */
18
+ allowed_workspace_ids: string[] | null;
19
+ /** Tools that may receive the value at session start (the tool token must carry the tool too). */
20
+ allowed_tools: ToolName[];
21
+ }
22
+ export interface Secret {
23
+ id: string;
24
+ organization_id: string;
25
+ project_id: string | null;
26
+ scope: SecretScope;
27
+ /** The environment variable name the value is injected as. */
28
+ name: string;
29
+ description: string;
30
+ current_version: number;
31
+ permissions: SecretPermissions;
32
+ created_by: {
33
+ type: 'user' | 'api_key';
34
+ id: string;
35
+ };
36
+ created_at: string;
37
+ updated_at: string;
38
+ rotated_at: string | null;
39
+ deleted_at: string | null;
40
+ }
41
+ export interface SecretVersion {
42
+ secret_id: string;
43
+ version: number;
44
+ state: 'current' | 'destroyed';
45
+ created_by: {
46
+ type: 'user' | 'api_key';
47
+ id: string;
48
+ };
49
+ created_at: string;
50
+ destroyed_at: string | null;
51
+ destroyed_reason: 'rotated' | 'deleted' | null;
52
+ }
53
+ export interface CreateSecretParams {
54
+ /** `^[A-Z_][A-Z0-9_]{0,127}$`, unique per project; `SHARDFLUX_` prefix reserved. */
55
+ name: string;
56
+ /** UTF-8 text up to 65536 bytes, no NUL. Never returned by the API. */
57
+ value: string;
58
+ description?: string;
59
+ /** Default null (any workspace of the project). */
60
+ allowedWorkspaceIds?: string[] | null;
61
+ /** Default ['exec', 'pty']. */
62
+ allowedTools?: ToolName[];
63
+ /** Defaults to a fresh key per call so transport retries replay instead of duplicating. */
64
+ idempotencyKey?: string;
65
+ }
66
+ export interface UpdateSecretParams {
67
+ description?: string;
68
+ allowedWorkspaceIds?: string[] | null;
69
+ allowedTools?: ToolName[];
70
+ }
71
+ export declare class SecretsApi {
72
+ #private;
73
+ constructor(ctx: () => ClientContext);
74
+ /** Creates a project secret (the key's own project). */
75
+ create(projectId: string, params: CreateSecretParams): Promise<Secret>;
76
+ /** Lists a project's secrets (metadata only). */
77
+ list(projectId: string, opts?: {
78
+ limit?: number;
79
+ cursor?: string;
80
+ includeDeleted?: boolean;
81
+ }): Promise<Page<Secret>>;
82
+ /** Metadata of one secret (deleted secrets stay readable with deleted_at). */
83
+ get(secretId: string): Promise<Secret>;
84
+ /** Changes description or usage permissions; applies from the next session start. */
85
+ update(secretId: string, params: UpdateSecretParams): Promise<Secret>;
86
+ /** Stores a new value as the next version; older values are erased. Running processes keep what they read. */
87
+ rotate(secretId: string, value: string, opts?: {
88
+ idempotencyKey?: string;
89
+ }): Promise<{
90
+ secret: Secret;
91
+ version: SecretVersion;
92
+ }>;
93
+ /** Version metadata, newest first. */
94
+ versions(secretId: string, opts?: {
95
+ limit?: number;
96
+ cursor?: string;
97
+ }): Promise<Page<SecretVersion>>;
98
+ /** Deletes (tombstones) the secret and erases its values; resolution stops at once. */
99
+ delete(secretId: string): Promise<void>;
100
+ }
@@ -0,0 +1,56 @@
1
+ import { randomId } from "./http.js";
2
+ const toPage = (p) => ({ data: p.data, nextCursor: p.next_cursor });
3
+ export class SecretsApi {
4
+ #ctx;
5
+ constructor(ctx) {
6
+ this.#ctx = ctx;
7
+ }
8
+ get #auth() {
9
+ return this.#ctx().authorization;
10
+ }
11
+ /** Creates a project secret (the key's own project). */
12
+ async create(projectId, params) {
13
+ return this.#ctx().http.json('POST', `/v1/projects/${encodeURIComponent(projectId)}/secrets`, {
14
+ json: {
15
+ name: params.name,
16
+ value: params.value,
17
+ ...(params.description !== undefined ? { description: params.description } : {}),
18
+ ...(params.allowedWorkspaceIds !== undefined ? { allowed_workspace_ids: params.allowedWorkspaceIds } : {}),
19
+ ...(params.allowedTools !== undefined ? { allowed_tools: params.allowedTools } : {}),
20
+ },
21
+ idempotencyKey: params.idempotencyKey ?? randomId('sdk-secret-'),
22
+ }, this.#auth);
23
+ }
24
+ /** Lists a project's secrets (metadata only). */
25
+ async list(projectId, opts = {}) {
26
+ const raw = await this.#ctx().http.json('GET', `/v1/projects/${encodeURIComponent(projectId)}/secrets`, { query: { limit: opts.limit, cursor: opts.cursor, include_deleted: opts.includeDeleted } }, this.#auth);
27
+ return toPage(raw);
28
+ }
29
+ /** Metadata of one secret (deleted secrets stay readable with deleted_at). */
30
+ get(secretId) {
31
+ return this.#ctx().http.json('GET', `/v1/secrets/${encodeURIComponent(secretId)}`, {}, this.#auth);
32
+ }
33
+ /** Changes description or usage permissions; applies from the next session start. */
34
+ update(secretId, params) {
35
+ return this.#ctx().http.json('PATCH', `/v1/secrets/${encodeURIComponent(secretId)}`, {
36
+ json: {
37
+ ...(params.description !== undefined ? { description: params.description } : {}),
38
+ ...(params.allowedWorkspaceIds !== undefined ? { allowed_workspace_ids: params.allowedWorkspaceIds } : {}),
39
+ ...(params.allowedTools !== undefined ? { allowed_tools: params.allowedTools } : {}),
40
+ },
41
+ }, this.#auth);
42
+ }
43
+ /** Stores a new value as the next version; older values are erased. Running processes keep what they read. */
44
+ rotate(secretId, value, opts = {}) {
45
+ return this.#ctx().http.json('POST', `/v1/secrets/${encodeURIComponent(secretId)}/versions`, { json: { value }, idempotencyKey: opts.idempotencyKey ?? randomId('sdk-rotate-') }, this.#auth);
46
+ }
47
+ /** Version metadata, newest first. */
48
+ async versions(secretId, opts = {}) {
49
+ const raw = await this.#ctx().http.json('GET', `/v1/secrets/${encodeURIComponent(secretId)}/versions`, { query: { limit: opts.limit, cursor: opts.cursor } }, this.#auth);
50
+ return toPage(raw);
51
+ }
52
+ /** Deletes (tombstones) the secret and erases its values; resolution stops at once. */
53
+ async delete(secretId) {
54
+ await this.#ctx().http.json('DELETE', `/v1/secrets/${encodeURIComponent(secretId)}`, {}, this.#auth);
55
+ }
56
+ }