@introspection-sdk/introspection-browser 0.5.0 → 0.5.2
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.
- package/.turbo/turbo-build.log +1 -1
- package/README.md +59 -0
- package/dist/api/client.d.ts +53 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +74 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/http.d.ts +36 -0
- package/dist/api/http.d.ts.map +1 -0
- package/dist/api/http.js +34 -0
- package/dist/api/http.js.map +1 -0
- package/dist/api/index.d.ts +36 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +35 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/sse.d.ts +7 -0
- package/dist/api/sse.d.ts.map +1 -0
- package/dist/api/sse.js +7 -0
- package/dist/api/sse.js.map +1 -0
- package/dist/api/tasks.d.ts +83 -0
- package/dist/api/tasks.d.ts.map +1 -0
- package/dist/api/tasks.js +146 -0
- package/dist/api/tasks.js.map +1 -0
- package/package.json +12 -7
- package/src/api/client.ts +98 -0
- package/src/api/http.ts +55 -0
- package/src/api/index.ts +70 -0
- package/src/api/sse.ts +6 -0
- package/src/api/tasks.ts +208 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
> @introspection-sdk/introspection-browser@0.5.
|
|
2
|
+
> @introspection-sdk/introspection-browser@0.5.2 build /home/runner/work/introspection-js-sdk/introspection-js-sdk/packages/introspection-browser
|
|
3
3
|
> tsc
|
|
4
4
|
|
package/README.md
CHANGED
|
@@ -30,3 +30,62 @@ client.feedback("thumbs_down", {
|
|
|
30
30
|
comments: "Off topic",
|
|
31
31
|
});
|
|
32
32
|
```
|
|
33
|
+
|
|
34
|
+
## Client-side API (`/api`)
|
|
35
|
+
|
|
36
|
+
The `@introspection-sdk/introspection-browser/api` entry point lets a
|
|
37
|
+
single-page app **create and stream Introspection tasks directly from the
|
|
38
|
+
browser, with no API key in JavaScript**. Authentication is the standard B2B2C
|
|
39
|
+
flow (see the [`sample-auth`](../../examples/apps/sample-auth) example):
|
|
40
|
+
|
|
41
|
+
1. The app's **own backend ("broker")** mints an Introspection access token —
|
|
42
|
+
via RFC 8693 token-exchange of the partner IdP token, a PKCE
|
|
43
|
+
`authorization_code`, or `client_credentials`. The IdP secret never leaves
|
|
44
|
+
the backend.
|
|
45
|
+
2. `connect()` redeems that token at the Data Plane
|
|
46
|
+
(`POST /v1/oauth/exchange`) for the HttpOnly `intro_dp_session` cookie.
|
|
47
|
+
3. Every subsequent call rides that cookie (`credentials: "include"`). When the
|
|
48
|
+
session expires, the client transparently re-mints it once on a `401`.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
|
|
52
|
+
|
|
53
|
+
const client = new IntrospectionApiClient({
|
|
54
|
+
dpUrl: "https://dp.us.introspection.dev",
|
|
55
|
+
projectId: "proj_…",
|
|
56
|
+
// your backend returns a fresh Introspection access token
|
|
57
|
+
getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await client.connect(); // → intro_dp_session cookie
|
|
61
|
+
|
|
62
|
+
const run = await client.tasks.start({
|
|
63
|
+
prompt: "Summarize my latest order",
|
|
64
|
+
agent_name: "support-agent", // or runtime_id: "rt_…"
|
|
65
|
+
identity: { user_id: "u_42" }, // folded into metadata.identity
|
|
66
|
+
visibility: "identity", // sharing scope; defaults by credential
|
|
67
|
+
idle_timeout_seconds: 120, // idle window before the sandbox is torn down
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
for await (const ev of run.stream()) {
|
|
71
|
+
console.log(ev.event, ev.data);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`client.tasks` exposes the full CRUD surface (`create` / `start` / `get` /
|
|
76
|
+
`list` / `update` / `delete` / `archive` / `unarchive`) plus per-run streaming
|
|
77
|
+
(`run.stream()`, `run.text()`, `run.cancel()`).
|
|
78
|
+
|
|
79
|
+
Both `create` and `start` accept two optional task controls:
|
|
80
|
+
|
|
81
|
+
- **`visibility`** (`"identity" | "member" | "project"`) — the task's minimum
|
|
82
|
+
sharing scope. Defaults to `"identity"` when the credential carries an
|
|
83
|
+
identity claim, else `"project"`. The owning `identity_key` is always derived
|
|
84
|
+
from the session JWT, never the request body.
|
|
85
|
+
- **`idle_timeout_seconds`** (`number`) — overrides the interactive idle window
|
|
86
|
+
before the sandbox is torn down. `0` tears it down as soon as it's
|
|
87
|
+
provisioned; omit to use the deployment default. Clamped to the task timeout.
|
|
88
|
+
|
|
89
|
+
> **CORS:** the Data Plane authorizes browser origins against its configured
|
|
90
|
+
> allowlist (`CORS_ORIGINS`). A new SPA origin must be present there for the
|
|
91
|
+
> cross-origin cookie exchange and task calls to succeed.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side Introspection API client.
|
|
3
|
+
*
|
|
4
|
+
* Lets a single-page app create and stream Introspection tasks directly
|
|
5
|
+
* from the browser, with no API key in JavaScript. The auth boundary:
|
|
6
|
+
*
|
|
7
|
+
* 1. The SPA's own backend ("broker") mints an Introspection access
|
|
8
|
+
* token — via RFC 8693 token-exchange of the partner IdP token, a
|
|
9
|
+
* PKCE `authorization_code`, or `client_credentials`. The IdP
|
|
10
|
+
* secret never leaves the backend. The SPA fetches that token
|
|
11
|
+
* through the `getToken` callback.
|
|
12
|
+
* 2. `connect()` redeems the token at the DP `POST /v1/oauth/exchange`
|
|
13
|
+
* for the HttpOnly `intro_dp_session` cookie.
|
|
14
|
+
* 3. Every subsequent call rides that cookie (`credentials: "include"`)
|
|
15
|
+
* — `client.tasks.start(...)`, `.get(...)`, run streaming, etc.
|
|
16
|
+
*
|
|
17
|
+
* When the session cookie expires, an in-flight request gets a 401, and
|
|
18
|
+
* the client transparently re-runs `getToken` + the DP exchange once
|
|
19
|
+
* before retrying.
|
|
20
|
+
*/
|
|
21
|
+
import { TasksClient } from "./tasks.js";
|
|
22
|
+
export interface IntrospectionApiClientOptions {
|
|
23
|
+
/** Data Plane REST base URL (e.g. `https://dp.us.introspection.dev`). */
|
|
24
|
+
dpUrl: string;
|
|
25
|
+
/** Project the session is scoped to. */
|
|
26
|
+
projectId: string;
|
|
27
|
+
/**
|
|
28
|
+
* Returns a fresh Introspection access token from the app's broker
|
|
29
|
+
* (its own backend). Called on `connect()` and again whenever the DP
|
|
30
|
+
* session cookie needs re-minting after a 401.
|
|
31
|
+
*/
|
|
32
|
+
getToken: () => string | Promise<string>;
|
|
33
|
+
/** Custom `fetch` (for tests or non-standard runtimes). */
|
|
34
|
+
fetch?: typeof fetch;
|
|
35
|
+
/** Extra headers merged into every DP request. */
|
|
36
|
+
additionalHeaders?: Record<string, string>;
|
|
37
|
+
}
|
|
38
|
+
export declare class IntrospectionApiClient {
|
|
39
|
+
private readonly opts;
|
|
40
|
+
/** `/v1/tasks` operations bound to the DP session cookie. */
|
|
41
|
+
readonly tasks: TasksClient;
|
|
42
|
+
private readonly fetchImpl;
|
|
43
|
+
constructor(opts: IntrospectionApiClientOptions);
|
|
44
|
+
/**
|
|
45
|
+
* Mint a token via `getToken` and redeem it at the DP for the
|
|
46
|
+
* `intro_dp_session` cookie. Call once before issuing task requests.
|
|
47
|
+
*/
|
|
48
|
+
connect(): Promise<void>;
|
|
49
|
+
private exchange;
|
|
50
|
+
/** 401 recovery: re-exchange, reporting success so the call can retry. */
|
|
51
|
+
private reexchange;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,6BAA6B;IAC5C,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED,qBAAa,sBAAsB;IAMrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAE5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAEZ,IAAI,EAAE,6BAA6B;IAgBhE;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAIhB,QAAQ;IAiBtB,0EAA0E;YAC5D,UAAU;CAQzB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side Introspection API client.
|
|
3
|
+
*
|
|
4
|
+
* Lets a single-page app create and stream Introspection tasks directly
|
|
5
|
+
* from the browser, with no API key in JavaScript. The auth boundary:
|
|
6
|
+
*
|
|
7
|
+
* 1. The SPA's own backend ("broker") mints an Introspection access
|
|
8
|
+
* token — via RFC 8693 token-exchange of the partner IdP token, a
|
|
9
|
+
* PKCE `authorization_code`, or `client_credentials`. The IdP
|
|
10
|
+
* secret never leaves the backend. The SPA fetches that token
|
|
11
|
+
* through the `getToken` callback.
|
|
12
|
+
* 2. `connect()` redeems the token at the DP `POST /v1/oauth/exchange`
|
|
13
|
+
* for the HttpOnly `intro_dp_session` cookie.
|
|
14
|
+
* 3. Every subsequent call rides that cookie (`credentials: "include"`)
|
|
15
|
+
* — `client.tasks.start(...)`, `.get(...)`, run streaming, etc.
|
|
16
|
+
*
|
|
17
|
+
* When the session cookie expires, an in-flight request gets a 401, and
|
|
18
|
+
* the client transparently re-runs `getToken` + the DP exchange once
|
|
19
|
+
* before retrying.
|
|
20
|
+
*/
|
|
21
|
+
import { BrowserHttpClient, stripTrailingSlash, toApiError } from "./http.js";
|
|
22
|
+
import { TasksClient } from "./tasks.js";
|
|
23
|
+
export class IntrospectionApiClient {
|
|
24
|
+
opts;
|
|
25
|
+
/** `/v1/tasks` operations bound to the DP session cookie. */
|
|
26
|
+
tasks;
|
|
27
|
+
fetchImpl;
|
|
28
|
+
constructor(opts) {
|
|
29
|
+
this.opts = opts;
|
|
30
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
31
|
+
if (!this.fetchImpl) {
|
|
32
|
+
throw new Error("global fetch is unavailable; pass `fetch` or run in a modern browser");
|
|
33
|
+
}
|
|
34
|
+
const http = new BrowserHttpClient({
|
|
35
|
+
apiUrl: opts.dpUrl,
|
|
36
|
+
additionalHeaders: opts.additionalHeaders,
|
|
37
|
+
fetch: opts.fetch,
|
|
38
|
+
onUnauthorized: () => this.reexchange(),
|
|
39
|
+
});
|
|
40
|
+
this.tasks = new TasksClient(http);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Mint a token via `getToken` and redeem it at the DP for the
|
|
44
|
+
* `intro_dp_session` cookie. Call once before issuing task requests.
|
|
45
|
+
*/
|
|
46
|
+
async connect() {
|
|
47
|
+
await this.exchange();
|
|
48
|
+
}
|
|
49
|
+
async exchange() {
|
|
50
|
+
const token = await this.opts.getToken();
|
|
51
|
+
const res = await this.fetchImpl(`${stripTrailingSlash(this.opts.dpUrl)}/v1/oauth/exchange`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
...(this.opts.additionalHeaders ?? {}),
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({ token, project_id: this.opts.projectId }),
|
|
58
|
+
credentials: "include",
|
|
59
|
+
});
|
|
60
|
+
if (!res.ok)
|
|
61
|
+
throw await toApiError(res);
|
|
62
|
+
}
|
|
63
|
+
/** 401 recovery: re-exchange, reporting success so the call can retry. */
|
|
64
|
+
async reexchange() {
|
|
65
|
+
try {
|
|
66
|
+
await this.exchange();
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAmBzC,MAAM,OAAO,sBAAsB;IAMJ;IAL7B,6DAA6D;IACpD,KAAK,CAAc;IAEX,SAAS,CAAe;IAEzC,YAA6B,IAAmC;QAAnC,SAAI,GAAJ,IAAI,CAA+B;QAC9D,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,iBAAiB,CAAC;YACjC,MAAM,EAAE,IAAI,CAAC,KAAK;YAClB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;YACzC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE;SACxC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAC9B,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAC1D;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC;aACvC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAChE,WAAW,EAAE,SAAS;SACvB,CACF,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,0EAA0E;IAClE,KAAK,CAAC,UAAU;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser HTTP transport for the Introspection Data Plane `/v1` surface.
|
|
3
|
+
*
|
|
4
|
+
* The request/response machinery — URL joining, query building, body
|
|
5
|
+
* serialization, error mapping, SSE — lives in the shared
|
|
6
|
+
* `@introspection-sdk/http` {@link BaseHttpClient}. This module pins the
|
|
7
|
+
* browser auth strategy on top of it: the DP is reached with the HttpOnly
|
|
8
|
+
* `intro_dp_session` cookie minted by `POST /v1/oauth/exchange`, so every
|
|
9
|
+
* request opts into `credentials: "include"` and sends NO bearer token —
|
|
10
|
+
* no Introspection credential ever lives in JavaScript. A 401 (expired
|
|
11
|
+
* session cookie) triggers a single `onUnauthorized` refresh + retry.
|
|
12
|
+
*/
|
|
13
|
+
import { BaseHttpClient, stripTrailingSlash, toApiError } from "@introspection-sdk/http";
|
|
14
|
+
export { stripTrailingSlash, toApiError };
|
|
15
|
+
export interface BrowserHttpConfig {
|
|
16
|
+
/** DP REST base URL every request is prefixed with. */
|
|
17
|
+
apiUrl: string;
|
|
18
|
+
/** Extra headers merged into every request. */
|
|
19
|
+
additionalHeaders?: Record<string, string>;
|
|
20
|
+
/** Custom `fetch` (for tests or non-standard runtimes). */
|
|
21
|
+
fetch?: typeof fetch;
|
|
22
|
+
/**
|
|
23
|
+
* Invoked when a request comes back `401`. Return `true` if the DP
|
|
24
|
+
* session was refreshed and the request should be retried once;
|
|
25
|
+
* `false` to surface the original error.
|
|
26
|
+
*/
|
|
27
|
+
onUnauthorized?: () => Promise<boolean>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cookie-authenticated HTTP wrapper bound to one DP endpoint. Has no
|
|
31
|
+
* opinion about which resource it serves — the caller picks the path.
|
|
32
|
+
*/
|
|
33
|
+
export declare class BrowserHttpClient extends BaseHttpClient {
|
|
34
|
+
constructor(cfg: BrowserHttpConfig);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/api/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,UAAU,EACX,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;gBACvC,GAAG,EAAE,iBAAiB;CAYnC"}
|
package/dist/api/http.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser HTTP transport for the Introspection Data Plane `/v1` surface.
|
|
3
|
+
*
|
|
4
|
+
* The request/response machinery — URL joining, query building, body
|
|
5
|
+
* serialization, error mapping, SSE — lives in the shared
|
|
6
|
+
* `@introspection-sdk/http` {@link BaseHttpClient}. This module pins the
|
|
7
|
+
* browser auth strategy on top of it: the DP is reached with the HttpOnly
|
|
8
|
+
* `intro_dp_session` cookie minted by `POST /v1/oauth/exchange`, so every
|
|
9
|
+
* request opts into `credentials: "include"` and sends NO bearer token —
|
|
10
|
+
* no Introspection credential ever lives in JavaScript. A 401 (expired
|
|
11
|
+
* session cookie) triggers a single `onUnauthorized` refresh + retry.
|
|
12
|
+
*/
|
|
13
|
+
import { BaseHttpClient, stripTrailingSlash, toApiError, } from "@introspection-sdk/http";
|
|
14
|
+
// Re-exported for `client.ts`, which redeems the DP session on `connect()`.
|
|
15
|
+
export { stripTrailingSlash, toApiError };
|
|
16
|
+
/**
|
|
17
|
+
* Cookie-authenticated HTTP wrapper bound to one DP endpoint. Has no
|
|
18
|
+
* opinion about which resource it serves — the caller picks the path.
|
|
19
|
+
*/
|
|
20
|
+
export class BrowserHttpClient extends BaseHttpClient {
|
|
21
|
+
constructor(cfg) {
|
|
22
|
+
super({
|
|
23
|
+
apiUrl: cfg.apiUrl,
|
|
24
|
+
additionalHeaders: cfg.additionalHeaders,
|
|
25
|
+
fetch: cfg.fetch,
|
|
26
|
+
transport: {
|
|
27
|
+
authHeaders: () => ({}),
|
|
28
|
+
credentials: "include",
|
|
29
|
+
onUnauthorized: cfg.onUnauthorized,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/api/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAEjC,4EAA4E;AAC5E,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,CAAC;AAiB1C;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,cAAc;IACnD,YAAY,GAAsB;QAChC,KAAK,CAAC;YACJ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;YACxC,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,SAAS,EAAE;gBACT,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;gBACvB,WAAW,EAAE,SAAS;gBACtB,cAAc,EAAE,GAAG,CAAC,cAAc;aACnC;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Introspection Browser API surface — `@introspection-sdk/introspection-browser/api`.
|
|
3
|
+
*
|
|
4
|
+
* A cookie-authenticated client for creating and streaming Introspection
|
|
5
|
+
* tasks directly from a single-page app, with no API key in the browser.
|
|
6
|
+
* Separate from the package's default telemetry export so apps only pull
|
|
7
|
+
* in what they use.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
|
|
12
|
+
*
|
|
13
|
+
* const client = new IntrospectionApiClient({
|
|
14
|
+
* dpUrl: "https://dp.us.introspection.dev",
|
|
15
|
+
* projectId: "proj_…",
|
|
16
|
+
* // your backend mints the Introspection access token
|
|
17
|
+
* getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* await client.connect(); // → intro_dp_session cookie
|
|
21
|
+
* const run = await client.tasks.start({
|
|
22
|
+
* prompt: "Summarize my latest order",
|
|
23
|
+
* agent_name: "support-agent",
|
|
24
|
+
* identity: { user_id: "u_42" },
|
|
25
|
+
* });
|
|
26
|
+
* for await (const ev of run.stream()) console.log(ev.event, ev.data);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export { IntrospectionApiClient, type IntrospectionApiClientOptions, } from "./client.js";
|
|
30
|
+
export { TasksClient, TaskRunsClient, RunHandle, type CreateTaskParams, type StartTaskParams, } from "./tasks.js";
|
|
31
|
+
export { BrowserHttpClient, type BrowserHttpConfig } from "./http.js";
|
|
32
|
+
export { parseSse } from "./sse.js";
|
|
33
|
+
export { Paginator, cursorPaginate, type PageSource, } from "@introspection-sdk/http";
|
|
34
|
+
export type { Task, TaskRun, TaskStatus, TaskVisibility, TaskCreateResponse, TaskListParams, TaskUpdateParams, TaskRunCreateParams, TaskCancelResponse, RunIdentityInput, Paginated, SseEvent, } from "@introspection-sdk/types";
|
|
35
|
+
export { IntrospectionAPIError, AuthenticationError, InsufficientScopeError, NotFoundError, NetworkError, } from "@introspection-sdk/types";
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EACL,SAAS,EACT,cAAc,EACd,KAAK,UAAU,GAChB,MAAM,yBAAyB,CAAC;AAGjC,YAAY,EACV,IAAI,EACJ,OAAO,EACP,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACT,QAAQ,GACT,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,YAAY,GACb,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Introspection Browser API surface — `@introspection-sdk/introspection-browser/api`.
|
|
3
|
+
*
|
|
4
|
+
* A cookie-authenticated client for creating and streaming Introspection
|
|
5
|
+
* tasks directly from a single-page app, with no API key in the browser.
|
|
6
|
+
* Separate from the package's default telemetry export so apps only pull
|
|
7
|
+
* in what they use.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
|
|
12
|
+
*
|
|
13
|
+
* const client = new IntrospectionApiClient({
|
|
14
|
+
* dpUrl: "https://dp.us.introspection.dev",
|
|
15
|
+
* projectId: "proj_…",
|
|
16
|
+
* // your backend mints the Introspection access token
|
|
17
|
+
* getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* await client.connect(); // → intro_dp_session cookie
|
|
21
|
+
* const run = await client.tasks.start({
|
|
22
|
+
* prompt: "Summarize my latest order",
|
|
23
|
+
* agent_name: "support-agent",
|
|
24
|
+
* identity: { user_id: "u_42" },
|
|
25
|
+
* });
|
|
26
|
+
* for await (const ev of run.stream()) console.log(ev.event, ev.data);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export { IntrospectionApiClient, } from "./client.js";
|
|
30
|
+
export { TasksClient, TaskRunsClient, RunHandle, } from "./tasks.js";
|
|
31
|
+
export { BrowserHttpClient } from "./http.js";
|
|
32
|
+
export { parseSse } from "./sse.js";
|
|
33
|
+
export { Paginator, cursorPaginate, } from "@introspection-sdk/http";
|
|
34
|
+
export { IntrospectionAPIError, AuthenticationError, InsufficientScopeError, NotFoundError, NetworkError, } from "@introspection-sdk/types";
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EACL,sBAAsB,GAEvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,GAGV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAA0B,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EACL,SAAS,EACT,cAAc,GAEf,MAAM,yBAAyB,CAAC;AAiBjC,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,aAAa,EACb,YAAY,GACb,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE parsing now lives in the shared `@introspection-sdk/http` core so
|
|
3
|
+
* the browser and Node SDKs stay byte-for-byte identical. Re-exported
|
|
4
|
+
* here to keep the existing `./sse.js` import path stable.
|
|
5
|
+
*/
|
|
6
|
+
export { parseSse } from "@introspection-sdk/http";
|
|
7
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/api/sse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/api/sse.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE parsing now lives in the shared `@introspection-sdk/http` core so
|
|
3
|
+
* the browser and Node SDKs stay byte-for-byte identical. Re-exported
|
|
4
|
+
* here to keep the existing `./sse.js` import path stable.
|
|
5
|
+
*/
|
|
6
|
+
export { parseSse } from "@introspection-sdk/http";
|
|
7
|
+
//# sourceMappingURL=sse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/api/sse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { RunIdentityInput, SseEvent, Task, TaskCancelResponse, TaskCreateResponse, TaskListParams, TaskRun, TaskRunCreateParams, TaskUpdateParams, TaskVisibility } from "@introspection-sdk/types";
|
|
2
|
+
import { Paginator } from "@introspection-sdk/http";
|
|
3
|
+
import { BrowserHttpClient } from "./http.js";
|
|
4
|
+
/**
|
|
5
|
+
* Body for creating a task directly against the DP from the browser.
|
|
6
|
+
*
|
|
7
|
+
* A browser caller has no pre-pinned `Runner`, so — unlike the Node
|
|
8
|
+
* SDK's runner-bound create — it selects the agent itself: pass
|
|
9
|
+
* `runtime_id` to pin a recipe runtime, or `agent_name` to fall back to
|
|
10
|
+
* a named recipe agent. `identity` is a convenience that is folded into
|
|
11
|
+
* `metadata.identity` for attribution (the DP derives the owning
|
|
12
|
+
* `identity_key` from the session's JWT claims, never from this body).
|
|
13
|
+
*/
|
|
14
|
+
export interface CreateTaskParams {
|
|
15
|
+
prompt?: string;
|
|
16
|
+
/** Pin the task to a specific recipe runtime. */
|
|
17
|
+
runtime_id?: string;
|
|
18
|
+
/** Named recipe agent, when no `runtime_id` is pinned. */
|
|
19
|
+
agent_name?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
22
|
+
visibility?: TaskVisibility;
|
|
23
|
+
/**
|
|
24
|
+
* Override the interactive idle window (seconds) before the sandbox is
|
|
25
|
+
* torn down. `0` tears down as soon as it's provisioned; omit to use the
|
|
26
|
+
* deployment default. Clamped to the task timeout.
|
|
27
|
+
*/
|
|
28
|
+
idle_timeout_seconds?: number;
|
|
29
|
+
/** Caller identity for attribution; merged into `metadata.identity`. */
|
|
30
|
+
identity?: RunIdentityInput;
|
|
31
|
+
}
|
|
32
|
+
export interface StartTaskParams extends CreateTaskParams {
|
|
33
|
+
/** Required for the `start()` sugar that returns a run handle. */
|
|
34
|
+
prompt: string;
|
|
35
|
+
}
|
|
36
|
+
/** Live handle on a task's run: stream its events, collect text, cancel. */
|
|
37
|
+
export declare class RunHandle {
|
|
38
|
+
readonly task: Task | null;
|
|
39
|
+
readonly run: TaskRun;
|
|
40
|
+
private readonly runs;
|
|
41
|
+
constructor(task: Task | null, run: TaskRun, runs: TaskRunsClient);
|
|
42
|
+
stream(): AsyncIterable<SseEvent>;
|
|
43
|
+
cancel(): Promise<TaskCancelResponse>;
|
|
44
|
+
/** Convenience: concatenate `data` from `text`/`message` SSE frames. */
|
|
45
|
+
text(): Promise<string>;
|
|
46
|
+
}
|
|
47
|
+
export declare class TaskRunsClient {
|
|
48
|
+
private readonly http;
|
|
49
|
+
constructor(http: BrowserHttpClient);
|
|
50
|
+
create(taskId: string, body: TaskRunCreateParams): Promise<RunHandle>;
|
|
51
|
+
get(taskId: string, runId: string): Promise<TaskRun>;
|
|
52
|
+
cancel(taskId: string, runId: string): Promise<TaskCancelResponse>;
|
|
53
|
+
stream(taskId: string, runId: string): AsyncIterable<SseEvent>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Cookie-authenticated `/v1/tasks` client. Mirrors the Node SDK's
|
|
57
|
+
* `TasksApi` shape but is bound to a DP session cookie rather than a
|
|
58
|
+
* bearer token, and lets the caller select the agent on create.
|
|
59
|
+
*/
|
|
60
|
+
export declare class TasksClient {
|
|
61
|
+
private readonly http;
|
|
62
|
+
readonly runs: TaskRunsClient;
|
|
63
|
+
constructor(http: BrowserHttpClient);
|
|
64
|
+
/**
|
|
65
|
+
* Tasks matching `params`. `await` it for the first page (preserving
|
|
66
|
+
* the wire envelope's counts + `next` cursor), or `for await` it to
|
|
67
|
+
* stream every task across pages (fetched lazily — `limit` sets the
|
|
68
|
+
* page size, `next` the starting cursor; stop early to stop fetching).
|
|
69
|
+
*
|
|
70
|
+
* Mirrors the Node SDK's `TasksApi.list`, which is the source of truth
|
|
71
|
+
* for the `/v1/tasks` shape.
|
|
72
|
+
*/
|
|
73
|
+
list(params?: TaskListParams): Paginator<Task>;
|
|
74
|
+
create(body: CreateTaskParams): Promise<TaskCreateResponse>;
|
|
75
|
+
get(taskId: string): Promise<Task>;
|
|
76
|
+
update(taskId: string, body: TaskUpdateParams): Promise<Task>;
|
|
77
|
+
delete(taskId: string): Promise<void>;
|
|
78
|
+
archive(taskId: string): Promise<void>;
|
|
79
|
+
unarchive(taskId: string): Promise<void>;
|
|
80
|
+
/** Sugar: create a task + return a handle on its initial run. */
|
|
81
|
+
start(params: StartTaskParams): Promise<RunHandle>;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/api/tasks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { cursorPaginate } from "@introspection-sdk/http";
|
|
2
|
+
import { parseSse } from "./sse.js";
|
|
3
|
+
function toTaskBody(params) {
|
|
4
|
+
const { identity, metadata, ...rest } = params;
|
|
5
|
+
const body = { ...rest };
|
|
6
|
+
const mergedMetadata = identity
|
|
7
|
+
? { ...(metadata ?? {}), identity }
|
|
8
|
+
: metadata;
|
|
9
|
+
if (mergedMetadata !== undefined)
|
|
10
|
+
body.metadata = mergedMetadata;
|
|
11
|
+
return body;
|
|
12
|
+
}
|
|
13
|
+
/** Live handle on a task's run: stream its events, collect text, cancel. */
|
|
14
|
+
export class RunHandle {
|
|
15
|
+
task;
|
|
16
|
+
run;
|
|
17
|
+
runs;
|
|
18
|
+
constructor(task, run, runs) {
|
|
19
|
+
this.task = task;
|
|
20
|
+
this.run = run;
|
|
21
|
+
this.runs = runs;
|
|
22
|
+
}
|
|
23
|
+
stream() {
|
|
24
|
+
return this.runs.stream(this.run.task_id, this.run.id);
|
|
25
|
+
}
|
|
26
|
+
cancel() {
|
|
27
|
+
return this.runs.cancel(this.run.task_id, this.run.id);
|
|
28
|
+
}
|
|
29
|
+
/** Convenience: concatenate `data` from `text`/`message` SSE frames. */
|
|
30
|
+
async text() {
|
|
31
|
+
let out = "";
|
|
32
|
+
for await (const ev of this.stream()) {
|
|
33
|
+
if (ev.event === "text" || ev.event === "message")
|
|
34
|
+
out += ev.data;
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class TaskRunsClient {
|
|
40
|
+
http;
|
|
41
|
+
constructor(http) {
|
|
42
|
+
this.http = http;
|
|
43
|
+
}
|
|
44
|
+
async create(taskId, body) {
|
|
45
|
+
const res = await this.http.request({
|
|
46
|
+
method: "POST",
|
|
47
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs`,
|
|
48
|
+
body,
|
|
49
|
+
});
|
|
50
|
+
return new RunHandle(null, res.run, this);
|
|
51
|
+
}
|
|
52
|
+
get(taskId, runId) {
|
|
53
|
+
return this.http.request({
|
|
54
|
+
method: "GET",
|
|
55
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
cancel(taskId, runId) {
|
|
59
|
+
return this.http.request({
|
|
60
|
+
method: "POST",
|
|
61
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/cancel`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async *stream(taskId, runId) {
|
|
65
|
+
const res = await this.http.stream({
|
|
66
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/stream`,
|
|
67
|
+
});
|
|
68
|
+
yield* parseSse(res);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Cookie-authenticated `/v1/tasks` client. Mirrors the Node SDK's
|
|
73
|
+
* `TasksApi` shape but is bound to a DP session cookie rather than a
|
|
74
|
+
* bearer token, and lets the caller select the agent on create.
|
|
75
|
+
*/
|
|
76
|
+
export class TasksClient {
|
|
77
|
+
http;
|
|
78
|
+
runs;
|
|
79
|
+
constructor(http) {
|
|
80
|
+
this.http = http;
|
|
81
|
+
this.runs = new TaskRunsClient(http);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Tasks matching `params`. `await` it for the first page (preserving
|
|
85
|
+
* the wire envelope's counts + `next` cursor), or `for await` it to
|
|
86
|
+
* stream every task across pages (fetched lazily — `limit` sets the
|
|
87
|
+
* page size, `next` the starting cursor; stop early to stop fetching).
|
|
88
|
+
*
|
|
89
|
+
* Mirrors the Node SDK's `TasksApi.list`, which is the source of truth
|
|
90
|
+
* for the `/v1/tasks` shape.
|
|
91
|
+
*/
|
|
92
|
+
list(params) {
|
|
93
|
+
return cursorPaginate((next) => this.http.request({
|
|
94
|
+
method: "GET",
|
|
95
|
+
path: "/v1/tasks",
|
|
96
|
+
query: { ...params, next },
|
|
97
|
+
}), params?.next);
|
|
98
|
+
}
|
|
99
|
+
create(body) {
|
|
100
|
+
return this.http.request({
|
|
101
|
+
method: "POST",
|
|
102
|
+
path: "/v1/tasks",
|
|
103
|
+
body: toTaskBody(body),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
get(taskId) {
|
|
107
|
+
return this.http.request({
|
|
108
|
+
method: "GET",
|
|
109
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
update(taskId, body) {
|
|
113
|
+
return this.http.request({
|
|
114
|
+
method: "PATCH",
|
|
115
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}`,
|
|
116
|
+
body,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
delete(taskId) {
|
|
120
|
+
return this.http.request({
|
|
121
|
+
method: "DELETE",
|
|
122
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}`,
|
|
123
|
+
expect: "empty",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
archive(taskId) {
|
|
127
|
+
return this.http.request({
|
|
128
|
+
method: "POST",
|
|
129
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/archive`,
|
|
130
|
+
expect: "empty",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
unarchive(taskId) {
|
|
134
|
+
return this.http.request({
|
|
135
|
+
method: "POST",
|
|
136
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/unarchive`,
|
|
137
|
+
expect: "empty",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/** Sugar: create a task + return a handle on its initial run. */
|
|
141
|
+
async start(params) {
|
|
142
|
+
const res = await this.create(params);
|
|
143
|
+
return new RunHandle(res.task, res.run, this.runs);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/api/tasks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@introspection-sdk/introspection-browser",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Introspection observability SDK for the browser",
|
|
6
6
|
"engines": {
|
|
@@ -13,21 +13,26 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./api": {
|
|
18
|
+
"types": "./dist/api/index.d.ts",
|
|
19
|
+
"default": "./dist/api/index.js"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"devDependencies": {
|
|
19
23
|
"@types/node": "^22.19.20",
|
|
20
24
|
"eslint": "^9.39.4",
|
|
21
25
|
"typescript": "5.9.3",
|
|
22
|
-
"@repo/
|
|
23
|
-
"@repo/
|
|
26
|
+
"@repo/eslint-config": "0.0.0",
|
|
27
|
+
"@repo/typescript-config": "0.0.0"
|
|
24
28
|
},
|
|
25
29
|
"dependencies": {
|
|
26
30
|
"@opentelemetry/api": "^1.9.1",
|
|
27
|
-
"@opentelemetry/api-logs": "^0.
|
|
28
|
-
"@opentelemetry/sdk-logs": "^0.
|
|
29
|
-
"@opentelemetry/exporter-logs-otlp-http": "^0.
|
|
30
|
-
"@introspection-sdk/
|
|
31
|
+
"@opentelemetry/api-logs": "^0.219.0",
|
|
32
|
+
"@opentelemetry/sdk-logs": "^0.219.0",
|
|
33
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.219.0",
|
|
34
|
+
"@introspection-sdk/http": "0.5.2",
|
|
35
|
+
"@introspection-sdk/types": "0.5.2"
|
|
31
36
|
},
|
|
32
37
|
"scripts": {
|
|
33
38
|
"lint": "eslint . --max-warnings 0",
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side Introspection API client.
|
|
3
|
+
*
|
|
4
|
+
* Lets a single-page app create and stream Introspection tasks directly
|
|
5
|
+
* from the browser, with no API key in JavaScript. The auth boundary:
|
|
6
|
+
*
|
|
7
|
+
* 1. The SPA's own backend ("broker") mints an Introspection access
|
|
8
|
+
* token — via RFC 8693 token-exchange of the partner IdP token, a
|
|
9
|
+
* PKCE `authorization_code`, or `client_credentials`. The IdP
|
|
10
|
+
* secret never leaves the backend. The SPA fetches that token
|
|
11
|
+
* through the `getToken` callback.
|
|
12
|
+
* 2. `connect()` redeems the token at the DP `POST /v1/oauth/exchange`
|
|
13
|
+
* for the HttpOnly `intro_dp_session` cookie.
|
|
14
|
+
* 3. Every subsequent call rides that cookie (`credentials: "include"`)
|
|
15
|
+
* — `client.tasks.start(...)`, `.get(...)`, run streaming, etc.
|
|
16
|
+
*
|
|
17
|
+
* When the session cookie expires, an in-flight request gets a 401, and
|
|
18
|
+
* the client transparently re-runs `getToken` + the DP exchange once
|
|
19
|
+
* before retrying.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { BrowserHttpClient, stripTrailingSlash, toApiError } from "./http.js";
|
|
23
|
+
import { TasksClient } from "./tasks.js";
|
|
24
|
+
|
|
25
|
+
export interface IntrospectionApiClientOptions {
|
|
26
|
+
/** Data Plane REST base URL (e.g. `https://dp.us.introspection.dev`). */
|
|
27
|
+
dpUrl: string;
|
|
28
|
+
/** Project the session is scoped to. */
|
|
29
|
+
projectId: string;
|
|
30
|
+
/**
|
|
31
|
+
* Returns a fresh Introspection access token from the app's broker
|
|
32
|
+
* (its own backend). Called on `connect()` and again whenever the DP
|
|
33
|
+
* session cookie needs re-minting after a 401.
|
|
34
|
+
*/
|
|
35
|
+
getToken: () => string | Promise<string>;
|
|
36
|
+
/** Custom `fetch` (for tests or non-standard runtimes). */
|
|
37
|
+
fetch?: typeof fetch;
|
|
38
|
+
/** Extra headers merged into every DP request. */
|
|
39
|
+
additionalHeaders?: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class IntrospectionApiClient {
|
|
43
|
+
/** `/v1/tasks` operations bound to the DP session cookie. */
|
|
44
|
+
readonly tasks: TasksClient;
|
|
45
|
+
|
|
46
|
+
private readonly fetchImpl: typeof fetch;
|
|
47
|
+
|
|
48
|
+
constructor(private readonly opts: IntrospectionApiClientOptions) {
|
|
49
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
50
|
+
if (!this.fetchImpl) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
"global fetch is unavailable; pass `fetch` or run in a modern browser",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const http = new BrowserHttpClient({
|
|
56
|
+
apiUrl: opts.dpUrl,
|
|
57
|
+
additionalHeaders: opts.additionalHeaders,
|
|
58
|
+
fetch: opts.fetch,
|
|
59
|
+
onUnauthorized: () => this.reexchange(),
|
|
60
|
+
});
|
|
61
|
+
this.tasks = new TasksClient(http);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Mint a token via `getToken` and redeem it at the DP for the
|
|
66
|
+
* `intro_dp_session` cookie. Call once before issuing task requests.
|
|
67
|
+
*/
|
|
68
|
+
async connect(): Promise<void> {
|
|
69
|
+
await this.exchange();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private async exchange(): Promise<void> {
|
|
73
|
+
const token = await this.opts.getToken();
|
|
74
|
+
const res = await this.fetchImpl(
|
|
75
|
+
`${stripTrailingSlash(this.opts.dpUrl)}/v1/oauth/exchange`,
|
|
76
|
+
{
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
...(this.opts.additionalHeaders ?? {}),
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({ token, project_id: this.opts.projectId }),
|
|
83
|
+
credentials: "include",
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
if (!res.ok) throw await toApiError(res);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 401 recovery: re-exchange, reporting success so the call can retry. */
|
|
90
|
+
private async reexchange(): Promise<boolean> {
|
|
91
|
+
try {
|
|
92
|
+
await this.exchange();
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/api/http.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser HTTP transport for the Introspection Data Plane `/v1` surface.
|
|
3
|
+
*
|
|
4
|
+
* The request/response machinery — URL joining, query building, body
|
|
5
|
+
* serialization, error mapping, SSE — lives in the shared
|
|
6
|
+
* `@introspection-sdk/http` {@link BaseHttpClient}. This module pins the
|
|
7
|
+
* browser auth strategy on top of it: the DP is reached with the HttpOnly
|
|
8
|
+
* `intro_dp_session` cookie minted by `POST /v1/oauth/exchange`, so every
|
|
9
|
+
* request opts into `credentials: "include"` and sends NO bearer token —
|
|
10
|
+
* no Introspection credential ever lives in JavaScript. A 401 (expired
|
|
11
|
+
* session cookie) triggers a single `onUnauthorized` refresh + retry.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
BaseHttpClient,
|
|
16
|
+
stripTrailingSlash,
|
|
17
|
+
toApiError,
|
|
18
|
+
} from "@introspection-sdk/http";
|
|
19
|
+
|
|
20
|
+
// Re-exported for `client.ts`, which redeems the DP session on `connect()`.
|
|
21
|
+
export { stripTrailingSlash, toApiError };
|
|
22
|
+
|
|
23
|
+
export interface BrowserHttpConfig {
|
|
24
|
+
/** DP REST base URL every request is prefixed with. */
|
|
25
|
+
apiUrl: string;
|
|
26
|
+
/** Extra headers merged into every request. */
|
|
27
|
+
additionalHeaders?: Record<string, string>;
|
|
28
|
+
/** Custom `fetch` (for tests or non-standard runtimes). */
|
|
29
|
+
fetch?: typeof fetch;
|
|
30
|
+
/**
|
|
31
|
+
* Invoked when a request comes back `401`. Return `true` if the DP
|
|
32
|
+
* session was refreshed and the request should be retried once;
|
|
33
|
+
* `false` to surface the original error.
|
|
34
|
+
*/
|
|
35
|
+
onUnauthorized?: () => Promise<boolean>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Cookie-authenticated HTTP wrapper bound to one DP endpoint. Has no
|
|
40
|
+
* opinion about which resource it serves — the caller picks the path.
|
|
41
|
+
*/
|
|
42
|
+
export class BrowserHttpClient extends BaseHttpClient {
|
|
43
|
+
constructor(cfg: BrowserHttpConfig) {
|
|
44
|
+
super({
|
|
45
|
+
apiUrl: cfg.apiUrl,
|
|
46
|
+
additionalHeaders: cfg.additionalHeaders,
|
|
47
|
+
fetch: cfg.fetch,
|
|
48
|
+
transport: {
|
|
49
|
+
authHeaders: () => ({}),
|
|
50
|
+
credentials: "include",
|
|
51
|
+
onUnauthorized: cfg.onUnauthorized,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/api/index.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Introspection Browser API surface — `@introspection-sdk/introspection-browser/api`.
|
|
3
|
+
*
|
|
4
|
+
* A cookie-authenticated client for creating and streaming Introspection
|
|
5
|
+
* tasks directly from a single-page app, with no API key in the browser.
|
|
6
|
+
* Separate from the package's default telemetry export so apps only pull
|
|
7
|
+
* in what they use.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { IntrospectionApiClient } from "@introspection-sdk/introspection-browser/api";
|
|
12
|
+
*
|
|
13
|
+
* const client = new IntrospectionApiClient({
|
|
14
|
+
* dpUrl: "https://dp.us.introspection.dev",
|
|
15
|
+
* projectId: "proj_…",
|
|
16
|
+
* // your backend mints the Introspection access token
|
|
17
|
+
* getToken: () => fetch("/api/introspection/token").then((r) => r.text()),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* await client.connect(); // → intro_dp_session cookie
|
|
21
|
+
* const run = await client.tasks.start({
|
|
22
|
+
* prompt: "Summarize my latest order",
|
|
23
|
+
* agent_name: "support-agent",
|
|
24
|
+
* identity: { user_id: "u_42" },
|
|
25
|
+
* });
|
|
26
|
+
* for await (const ev of run.stream()) console.log(ev.event, ev.data);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
IntrospectionApiClient,
|
|
32
|
+
type IntrospectionApiClientOptions,
|
|
33
|
+
} from "./client.js";
|
|
34
|
+
export {
|
|
35
|
+
TasksClient,
|
|
36
|
+
TaskRunsClient,
|
|
37
|
+
RunHandle,
|
|
38
|
+
type CreateTaskParams,
|
|
39
|
+
type StartTaskParams,
|
|
40
|
+
} from "./tasks.js";
|
|
41
|
+
export { BrowserHttpClient, type BrowserHttpConfig } from "./http.js";
|
|
42
|
+
export { parseSse } from "./sse.js";
|
|
43
|
+
export {
|
|
44
|
+
Paginator,
|
|
45
|
+
cursorPaginate,
|
|
46
|
+
type PageSource,
|
|
47
|
+
} from "@introspection-sdk/http";
|
|
48
|
+
|
|
49
|
+
// Re-exported wire types for convenience.
|
|
50
|
+
export type {
|
|
51
|
+
Task,
|
|
52
|
+
TaskRun,
|
|
53
|
+
TaskStatus,
|
|
54
|
+
TaskVisibility,
|
|
55
|
+
TaskCreateResponse,
|
|
56
|
+
TaskListParams,
|
|
57
|
+
TaskUpdateParams,
|
|
58
|
+
TaskRunCreateParams,
|
|
59
|
+
TaskCancelResponse,
|
|
60
|
+
RunIdentityInput,
|
|
61
|
+
Paginated,
|
|
62
|
+
SseEvent,
|
|
63
|
+
} from "@introspection-sdk/types";
|
|
64
|
+
export {
|
|
65
|
+
IntrospectionAPIError,
|
|
66
|
+
AuthenticationError,
|
|
67
|
+
InsufficientScopeError,
|
|
68
|
+
NotFoundError,
|
|
69
|
+
NetworkError,
|
|
70
|
+
} from "@introspection-sdk/types";
|
package/src/api/sse.ts
ADDED
package/src/api/tasks.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Paginated,
|
|
3
|
+
RunIdentityInput,
|
|
4
|
+
SseEvent,
|
|
5
|
+
Task,
|
|
6
|
+
TaskCancelResponse,
|
|
7
|
+
TaskCreateResponse,
|
|
8
|
+
TaskListParams,
|
|
9
|
+
TaskRun,
|
|
10
|
+
TaskRunCreateParams,
|
|
11
|
+
TaskRunResponse,
|
|
12
|
+
TaskUpdateParams,
|
|
13
|
+
TaskVisibility,
|
|
14
|
+
} from "@introspection-sdk/types";
|
|
15
|
+
import { Paginator, cursorPaginate } from "@introspection-sdk/http";
|
|
16
|
+
import { BrowserHttpClient } from "./http.js";
|
|
17
|
+
import { parseSse } from "./sse.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Body for creating a task directly against the DP from the browser.
|
|
21
|
+
*
|
|
22
|
+
* A browser caller has no pre-pinned `Runner`, so — unlike the Node
|
|
23
|
+
* SDK's runner-bound create — it selects the agent itself: pass
|
|
24
|
+
* `runtime_id` to pin a recipe runtime, or `agent_name` to fall back to
|
|
25
|
+
* a named recipe agent. `identity` is a convenience that is folded into
|
|
26
|
+
* `metadata.identity` for attribution (the DP derives the owning
|
|
27
|
+
* `identity_key` from the session's JWT claims, never from this body).
|
|
28
|
+
*/
|
|
29
|
+
export interface CreateTaskParams {
|
|
30
|
+
prompt?: string;
|
|
31
|
+
/** Pin the task to a specific recipe runtime. */
|
|
32
|
+
runtime_id?: string;
|
|
33
|
+
/** Named recipe agent, when no `runtime_id` is pinned. */
|
|
34
|
+
agent_name?: string;
|
|
35
|
+
title?: string;
|
|
36
|
+
metadata?: Record<string, unknown>;
|
|
37
|
+
visibility?: TaskVisibility;
|
|
38
|
+
/**
|
|
39
|
+
* Override the interactive idle window (seconds) before the sandbox is
|
|
40
|
+
* torn down. `0` tears down as soon as it's provisioned; omit to use the
|
|
41
|
+
* deployment default. Clamped to the task timeout.
|
|
42
|
+
*/
|
|
43
|
+
idle_timeout_seconds?: number;
|
|
44
|
+
/** Caller identity for attribution; merged into `metadata.identity`. */
|
|
45
|
+
identity?: RunIdentityInput;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface StartTaskParams extends CreateTaskParams {
|
|
49
|
+
/** Required for the `start()` sugar that returns a run handle. */
|
|
50
|
+
prompt: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function toTaskBody(params: CreateTaskParams): Record<string, unknown> {
|
|
54
|
+
const { identity, metadata, ...rest } = params;
|
|
55
|
+
const body: Record<string, unknown> = { ...rest };
|
|
56
|
+
const mergedMetadata = identity
|
|
57
|
+
? { ...(metadata ?? {}), identity }
|
|
58
|
+
: metadata;
|
|
59
|
+
if (mergedMetadata !== undefined) body.metadata = mergedMetadata;
|
|
60
|
+
return body;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Live handle on a task's run: stream its events, collect text, cancel. */
|
|
64
|
+
export class RunHandle {
|
|
65
|
+
constructor(
|
|
66
|
+
public readonly task: Task | null,
|
|
67
|
+
public readonly run: TaskRun,
|
|
68
|
+
private readonly runs: TaskRunsClient,
|
|
69
|
+
) {}
|
|
70
|
+
|
|
71
|
+
stream(): AsyncIterable<SseEvent> {
|
|
72
|
+
return this.runs.stream(this.run.task_id, this.run.id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
cancel(): Promise<TaskCancelResponse> {
|
|
76
|
+
return this.runs.cancel(this.run.task_id, this.run.id);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Convenience: concatenate `data` from `text`/`message` SSE frames. */
|
|
80
|
+
async text(): Promise<string> {
|
|
81
|
+
let out = "";
|
|
82
|
+
for await (const ev of this.stream()) {
|
|
83
|
+
if (ev.event === "text" || ev.event === "message") out += ev.data;
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class TaskRunsClient {
|
|
90
|
+
constructor(private readonly http: BrowserHttpClient) {}
|
|
91
|
+
|
|
92
|
+
async create(taskId: string, body: TaskRunCreateParams): Promise<RunHandle> {
|
|
93
|
+
const res = await this.http.request<TaskRunResponse>({
|
|
94
|
+
method: "POST",
|
|
95
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs`,
|
|
96
|
+
body,
|
|
97
|
+
});
|
|
98
|
+
return new RunHandle(null, res.run, this);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get(taskId: string, runId: string): Promise<TaskRun> {
|
|
102
|
+
return this.http.request<TaskRun>({
|
|
103
|
+
method: "GET",
|
|
104
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
cancel(taskId: string, runId: string): Promise<TaskCancelResponse> {
|
|
109
|
+
return this.http.request<TaskCancelResponse>({
|
|
110
|
+
method: "POST",
|
|
111
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/cancel`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async *stream(taskId: string, runId: string): AsyncIterable<SseEvent> {
|
|
116
|
+
const res = await this.http.stream({
|
|
117
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/runs/${encodeURIComponent(runId)}/stream`,
|
|
118
|
+
});
|
|
119
|
+
yield* parseSse(res);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Cookie-authenticated `/v1/tasks` client. Mirrors the Node SDK's
|
|
125
|
+
* `TasksApi` shape but is bound to a DP session cookie rather than a
|
|
126
|
+
* bearer token, and lets the caller select the agent on create.
|
|
127
|
+
*/
|
|
128
|
+
export class TasksClient {
|
|
129
|
+
readonly runs: TaskRunsClient;
|
|
130
|
+
|
|
131
|
+
constructor(private readonly http: BrowserHttpClient) {
|
|
132
|
+
this.runs = new TaskRunsClient(http);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Tasks matching `params`. `await` it for the first page (preserving
|
|
137
|
+
* the wire envelope's counts + `next` cursor), or `for await` it to
|
|
138
|
+
* stream every task across pages (fetched lazily — `limit` sets the
|
|
139
|
+
* page size, `next` the starting cursor; stop early to stop fetching).
|
|
140
|
+
*
|
|
141
|
+
* Mirrors the Node SDK's `TasksApi.list`, which is the source of truth
|
|
142
|
+
* for the `/v1/tasks` shape.
|
|
143
|
+
*/
|
|
144
|
+
list(params?: TaskListParams): Paginator<Task> {
|
|
145
|
+
return cursorPaginate(
|
|
146
|
+
(next) =>
|
|
147
|
+
this.http.request<Paginated<Task>>({
|
|
148
|
+
method: "GET",
|
|
149
|
+
path: "/v1/tasks",
|
|
150
|
+
query: { ...params, next } as Record<string, unknown>,
|
|
151
|
+
}),
|
|
152
|
+
params?.next,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
create(body: CreateTaskParams): Promise<TaskCreateResponse> {
|
|
157
|
+
return this.http.request<TaskCreateResponse>({
|
|
158
|
+
method: "POST",
|
|
159
|
+
path: "/v1/tasks",
|
|
160
|
+
body: toTaskBody(body),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
get(taskId: string): Promise<Task> {
|
|
165
|
+
return this.http.request<Task>({
|
|
166
|
+
method: "GET",
|
|
167
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
update(taskId: string, body: TaskUpdateParams): Promise<Task> {
|
|
172
|
+
return this.http.request<Task>({
|
|
173
|
+
method: "PATCH",
|
|
174
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}`,
|
|
175
|
+
body,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
delete(taskId: string): Promise<void> {
|
|
180
|
+
return this.http.request<void>({
|
|
181
|
+
method: "DELETE",
|
|
182
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}`,
|
|
183
|
+
expect: "empty",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
archive(taskId: string): Promise<void> {
|
|
188
|
+
return this.http.request<void>({
|
|
189
|
+
method: "POST",
|
|
190
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/archive`,
|
|
191
|
+
expect: "empty",
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
unarchive(taskId: string): Promise<void> {
|
|
196
|
+
return this.http.request<void>({
|
|
197
|
+
method: "POST",
|
|
198
|
+
path: `/v1/tasks/${encodeURIComponent(taskId)}/unarchive`,
|
|
199
|
+
expect: "empty",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Sugar: create a task + return a handle on its initial run. */
|
|
204
|
+
async start(params: StartTaskParams): Promise<RunHandle> {
|
|
205
|
+
const res = await this.create(params);
|
|
206
|
+
return new RunHandle(res.task, res.run, this.runs);
|
|
207
|
+
}
|
|
208
|
+
}
|