@pinecall/agents 0.9.20
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/LICENSE +202 -0
- package/README.md +121 -0
- package/dist/agent/accepts.d.ts +31 -0
- package/dist/agent/accepts.d.ts.map +1 -0
- package/dist/agent/accepts.js +27 -0
- package/dist/agent/accepts.js.map +1 -0
- package/dist/agent/agent.d.ts +151 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +240 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/authors.d.ts +12 -0
- package/dist/agent/authors.d.ts.map +1 -0
- package/dist/agent/authors.js +26 -0
- package/dist/agent/authors.js.map +1 -0
- package/dist/agent/decorators.d.ts +16 -0
- package/dist/agent/decorators.d.ts.map +1 -0
- package/dist/agent/decorators.js +43 -0
- package/dist/agent/decorators.js.map +1 -0
- package/dist/agent/docstrings.d.ts +35 -0
- package/dist/agent/docstrings.d.ts.map +1 -0
- package/dist/agent/docstrings.js +301 -0
- package/dist/agent/docstrings.js.map +1 -0
- package/dist/agent/knowledge.d.ts +9 -0
- package/dist/agent/knowledge.d.ts.map +1 -0
- package/dist/agent/knowledge.js +3 -0
- package/dist/agent/knowledge.js.map +1 -0
- package/dist/agent/lifecycle.d.ts +29 -0
- package/dist/agent/lifecycle.d.ts.map +1 -0
- package/dist/agent/lifecycle.js +9 -0
- package/dist/agent/lifecycle.js.map +1 -0
- package/dist/agent/searching.d.ts +7 -0
- package/dist/agent/searching.d.ts.map +1 -0
- package/dist/agent/searching.js +35 -0
- package/dist/agent/searching.js.map +1 -0
- package/dist/agent/stages.d.ts +23 -0
- package/dist/agent/stages.d.ts.map +1 -0
- package/dist/agent/stages.js +29 -0
- package/dist/agent/stages.js.map +1 -0
- package/dist/agent/state.d.ts +36 -0
- package/dist/agent/state.d.ts.map +1 -0
- package/dist/agent/state.js +93 -0
- package/dist/agent/state.js.map +1 -0
- package/dist/agent/tools.d.ts +61 -0
- package/dist/agent/tools.d.ts.map +1 -0
- package/dist/agent/tools.js +145 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/agent/view.d.ts +29 -0
- package/dist/agent/view.d.ts.map +1 -0
- package/dist/agent/view.js +29 -0
- package/dist/agent/view.js.map +1 -0
- package/dist/agent/visibility.d.ts +32 -0
- package/dist/agent/visibility.d.ts.map +1 -0
- package/dist/agent/visibility.js +77 -0
- package/dist/agent/visibility.js.map +1 -0
- package/dist/call/call.d.ts +137 -0
- package/dist/call/call.d.ts.map +1 -0
- package/dist/call/call.js +228 -0
- package/dist/call/call.js.map +1 -0
- package/dist/call/history.d.ts +28 -0
- package/dist/call/history.d.ts.map +1 -0
- package/dist/call/history.js +32 -0
- package/dist/call/history.js.map +1 -0
- package/dist/call/room.d.ts +56 -0
- package/dist/call/room.d.ts.map +1 -0
- package/dist/call/room.js +66 -0
- package/dist/call/room.js.map +1 -0
- package/dist/client/agent.d.ts +93 -0
- package/dist/client/agent.d.ts.map +1 -0
- package/dist/client/agent.js +230 -0
- package/dist/client/agent.js.map +1 -0
- package/dist/client/calls.d.ts +87 -0
- package/dist/client/calls.d.ts.map +1 -0
- package/dist/client/calls.js +180 -0
- package/dist/client/calls.js.map +1 -0
- package/dist/client/client.d.ts +112 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +284 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/connection.d.ts +52 -0
- package/dist/client/connection.d.ts.map +1 -0
- package/dist/client/connection.js +157 -0
- package/dist/client/connection.js.map +1 -0
- package/dist/client/endpoints.d.ts +9 -0
- package/dist/client/endpoints.d.ts.map +1 -0
- package/dist/client/endpoints.js +25 -0
- package/dist/client/endpoints.js.map +1 -0
- package/dist/client/frames.d.ts +29 -0
- package/dist/client/frames.d.ts.map +1 -0
- package/dist/client/frames.js +51 -0
- package/dist/client/frames.js.map +1 -0
- package/dist/client/index.d.ts +18 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +13 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/listeners.d.ts +35 -0
- package/dist/client/listeners.d.ts.map +1 -0
- package/dist/client/listeners.js +60 -0
- package/dist/client/listeners.js.map +1 -0
- package/dist/client/lost.d.ts +4 -0
- package/dist/client/lost.d.ts.map +1 -0
- package/dist/client/lost.js +8 -0
- package/dist/client/lost.js.map +1 -0
- package/dist/client/observe.d.ts +50 -0
- package/dist/client/observe.d.ts.map +1 -0
- package/dist/client/observe.js +125 -0
- package/dist/client/observe.js.map +1 -0
- package/dist/client/signed.d.ts +14 -0
- package/dist/client/signed.d.ts.map +1 -0
- package/dist/client/signed.js +18 -0
- package/dist/client/signed.js.map +1 -0
- package/dist/client/testing/gateway.d.ts +58 -0
- package/dist/client/testing/gateway.d.ts.map +1 -0
- package/dist/client/testing/gateway.js +175 -0
- package/dist/client/testing/gateway.js.map +1 -0
- package/dist/client/testing/index.d.ts +4 -0
- package/dist/client/testing/index.d.ts.map +1 -0
- package/dist/client/testing/index.js +4 -0
- package/dist/client/testing/index.js.map +1 -0
- package/dist/client/testing/log.d.ts +24 -0
- package/dist/client/testing/log.d.ts.map +1 -0
- package/dist/client/testing/log.js +106 -0
- package/dist/client/testing/log.js.map +1 -0
- package/dist/client/testing/loopback.d.ts +3 -0
- package/dist/client/testing/loopback.d.ts.map +1 -0
- package/dist/client/testing/loopback.js +3 -0
- package/dist/client/testing/loopback.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/connect.d.ts +42 -0
- package/dist/runtime/connect.d.ts.map +1 -0
- package/dist/runtime/connect.js +234 -0
- package/dist/runtime/connect.js.map +1 -0
- package/dist/runtime/dispatch.d.ts +32 -0
- package/dist/runtime/dispatch.d.ts.map +1 -0
- package/dist/runtime/dispatch.js +46 -0
- package/dist/runtime/dispatch.js.map +1 -0
- package/dist/runtime/environment.d.ts +7 -0
- package/dist/runtime/environment.d.ts.map +1 -0
- package/dist/runtime/environment.js +30 -0
- package/dist/runtime/environment.js.map +1 -0
- package/dist/runtime/recall.d.ts +9 -0
- package/dist/runtime/recall.d.ts.map +1 -0
- package/dist/runtime/recall.js +13 -0
- package/dist/runtime/recall.js.map +1 -0
- package/dist/runtime/run-tool.d.ts +18 -0
- package/dist/runtime/run-tool.d.ts.map +1 -0
- package/dist/runtime/run-tool.js +70 -0
- package/dist/runtime/run-tool.js.map +1 -0
- package/dist/serve/index.d.ts +5 -0
- package/dist/serve/index.d.ts.map +1 -0
- package/dist/serve/index.js +50 -0
- package/dist/serve/index.js.map +1 -0
- package/dist/serve/io.d.ts +14 -0
- package/dist/serve/io.d.ts.map +1 -0
- package/dist/serve/io.js +6 -0
- package/dist/serve/io.js.map +1 -0
- package/dist/serve/leaving.d.ts +12 -0
- package/dist/serve/leaving.d.ts.map +1 -0
- package/dist/serve/leaving.js +61 -0
- package/dist/serve/leaving.js.map +1 -0
- package/dist/serve/lines.d.ts +7 -0
- package/dist/serve/lines.d.ts.map +1 -0
- package/dist/serve/lines.js +25 -0
- package/dist/serve/lines.js.map +1 -0
- package/dist/serve/load.d.ts +26 -0
- package/dist/serve/load.d.ts.map +1 -0
- package/dist/serve/load.js +61 -0
- package/dist/serve/load.js.map +1 -0
- package/dist/serve/machine.d.ts +4 -0
- package/dist/serve/machine.d.ts.map +1 -0
- package/dist/serve/machine.js +33 -0
- package/dist/serve/machine.js.map +1 -0
- package/dist/serve/prompt.d.ts +7 -0
- package/dist/serve/prompt.d.ts.map +1 -0
- package/dist/serve/prompt.js +51 -0
- package/dist/serve/prompt.js.map +1 -0
- package/dist/serve/start.d.ts +7 -0
- package/dist/serve/start.d.ts.map +1 -0
- package/dist/serve/start.js +95 -0
- package/dist/serve/start.js.map +1 -0
- package/dist/serve/viewing.d.ts +17 -0
- package/dist/serve/viewing.d.ts.map +1 -0
- package/dist/serve/viewing.js +42 -0
- package/dist/serve/viewing.js.map +1 -0
- package/dist/views/built-in-rules.d.ts +9 -0
- package/dist/views/built-in-rules.d.ts.map +1 -0
- package/dist/views/built-in-rules.js +28 -0
- package/dist/views/built-in-rules.js.map +1 -0
- package/dist/views/components.d.ts +17 -0
- package/dist/views/components.d.ts.map +1 -0
- package/dist/views/components.js +33 -0
- package/dist/views/components.js.map +1 -0
- package/dist/views/jsx-dev-runtime.d.ts +3 -0
- package/dist/views/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/views/jsx-dev-runtime.js +4 -0
- package/dist/views/jsx-dev-runtime.js.map +1 -0
- package/dist/views/jsx-runtime.d.ts +46 -0
- package/dist/views/jsx-runtime.d.ts.map +1 -0
- package/dist/views/jsx-runtime.js +63 -0
- package/dist/views/jsx-runtime.js.map +1 -0
- package/dist/views/layout.d.ts +19 -0
- package/dist/views/layout.d.ts.map +1 -0
- package/dist/views/layout.js +58 -0
- package/dist/views/layout.js.map +1 -0
- package/dist/views/nodes.d.ts +48 -0
- package/dist/views/nodes.d.ts.map +1 -0
- package/dist/views/nodes.js +45 -0
- package/dist/views/nodes.js.map +1 -0
- package/dist/views/panels.d.ts +17 -0
- package/dist/views/panels.d.ts.map +1 -0
- package/dist/views/panels.js +62 -0
- package/dist/views/panels.js.map +1 -0
- package/dist/views/render.d.ts +11 -0
- package/dist/views/render.d.ts.map +1 -0
- package/dist/views/render.js +19 -0
- package/dist/views/render.js.map +1 -0
- package/dist/wire/agent-config.d.ts +213 -0
- package/dist/wire/agent-config.d.ts.map +1 -0
- package/dist/wire/agent-config.js +126 -0
- package/dist/wire/agent-config.js.map +1 -0
- package/dist/wire/codec.d.ts +43 -0
- package/dist/wire/codec.d.ts.map +1 -0
- package/dist/wire/codec.js +51 -0
- package/dist/wire/codec.js.map +1 -0
- package/dist/wire/commands.d.ts +443 -0
- package/dist/wire/commands.d.ts.map +1 -0
- package/dist/wire/commands.js +203 -0
- package/dist/wire/commands.js.map +1 -0
- package/dist/wire/defs.d.ts +373 -0
- package/dist/wire/defs.d.ts.map +1 -0
- package/dist/wire/defs.js +212 -0
- package/dist/wire/defs.js.map +1 -0
- package/dist/wire/envelope.d.ts +29 -0
- package/dist/wire/envelope.d.ts.map +1 -0
- package/dist/wire/envelope.js +27 -0
- package/dist/wire/envelope.js.map +1 -0
- package/dist/wire/events-call.d.ts +446 -0
- package/dist/wire/events-call.d.ts.map +1 -0
- package/dist/wire/events-call.js +196 -0
- package/dist/wire/events-call.js.map +1 -0
- package/dist/wire/events.d.ts +946 -0
- package/dist/wire/events.d.ts.map +1 -0
- package/dist/wire/events.js +309 -0
- package/dist/wire/events.js.map +1 -0
- package/dist/wire/index.d.ts +19 -0
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/wire/index.js +19 -0
- package/dist/wire/index.js.map +1 -0
- package/dist/wire/metrics.d.ts +366 -0
- package/dist/wire/metrics.d.ts.map +1 -0
- package/dist/wire/metrics.js +269 -0
- package/dist/wire/metrics.js.map +1 -0
- package/dist/wire/reduce.d.ts +10 -0
- package/dist/wire/reduce.d.ts.map +1 -0
- package/dist/wire/reduce.js +367 -0
- package/dist/wire/reduce.js.map +1 -0
- package/dist/wire/registry.d.ts +1789 -0
- package/dist/wire/registry.d.ts.map +1 -0
- package/dist/wire/registry.js +118 -0
- package/dist/wire/registry.js.map +1 -0
- package/dist/wire/rest-data.d.ts +170 -0
- package/dist/wire/rest-data.d.ts.map +1 -0
- package/dist/wire/rest-data.js +78 -0
- package/dist/wire/rest-data.js.map +1 -0
- package/dist/wire/rest-hosting.d.ts +74 -0
- package/dist/wire/rest-hosting.d.ts.map +1 -0
- package/dist/wire/rest-hosting.js +38 -0
- package/dist/wire/rest-hosting.js.map +1 -0
- package/dist/wire/rest-retrieval.d.ts +214 -0
- package/dist/wire/rest-retrieval.d.ts.map +1 -0
- package/dist/wire/rest-retrieval.js +161 -0
- package/dist/wire/rest-retrieval.js.map +1 -0
- package/dist/wire/rest.d.ts +786 -0
- package/dist/wire/rest.d.ts.map +1 -0
- package/dist/wire/rest.js +238 -0
- package/dist/wire/rest.js.map +1 -0
- package/dist/wire/room.d.ts +106 -0
- package/dist/wire/room.d.ts.map +1 -0
- package/dist/wire/room.js +73 -0
- package/dist/wire/room.js.map +1 -0
- package/dist/wire/state.d.ts +915 -0
- package/dist/wire/state.d.ts.map +1 -0
- package/dist/wire/state.js +189 -0
- package/dist/wire/state.js.map +1 -0
- package/dist/wire/verbs.d.ts +73 -0
- package/dist/wire/verbs.d.ts.map +1 -0
- package/dist/wire/verbs.js +50 -0
- package/dist/wire/verbs.js.map +1 -0
- package/package.json +74 -0
- package/tsconfig.tenant.json +24 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { Agent } from "./agent.js";
|
|
2
|
+
export type { AgentGateway, AgentOptions, DevHandler, Tool } from "./agent.js";
|
|
3
|
+
export { Call, CallBook } from "./calls.js";
|
|
4
|
+
export type { CallGateway, CallStatus } from "./calls.js";
|
|
5
|
+
export { Pinecall } from "./client.js";
|
|
6
|
+
export type { DrainOptions, Drained, Found, PinecallOptions } from "./client.js";
|
|
7
|
+
export { Connection } from "./connection.js";
|
|
8
|
+
export type { Backoff, ConnectionHandlers, ConnectionOptions } from "./connection.js";
|
|
9
|
+
export { agentLogUrl, appsUrl, callLogUrl, lookupUrl } from "./endpoints.js";
|
|
10
|
+
export { DevRefused, PinecallError, Refused, frame, nextId } from "./frames.js";
|
|
11
|
+
export { Listeners, camelEvent } from "./listeners.js";
|
|
12
|
+
export type { AnyListener, CamelEvent, Listener, Payload } from "./listeners.js";
|
|
13
|
+
export { aLostSocket } from "./lost.js";
|
|
14
|
+
export { history, observe } from "./observe.js";
|
|
15
|
+
export type { LogTarget, Observation, Page, ReadOptions } from "./observe.js";
|
|
16
|
+
export { signed } from "./signed.js";
|
|
17
|
+
export type { World } from "./signed.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @pinecall/agents/client: register agents over the gateway socket, answer tool calls, and read logs.
|
|
2
|
+
// Socket layer only, with no dependency on the Agent framework, views or CLI.
|
|
3
|
+
export { Agent } from "./agent.js";
|
|
4
|
+
export { Call, CallBook } from "./calls.js";
|
|
5
|
+
export { Pinecall } from "./client.js";
|
|
6
|
+
export { Connection } from "./connection.js";
|
|
7
|
+
export { agentLogUrl, appsUrl, callLogUrl, lookupUrl } from "./endpoints.js";
|
|
8
|
+
export { DevRefused, PinecallError, Refused, frame, nextId } from "./frames.js";
|
|
9
|
+
export { Listeners, camelEvent } from "./listeners.js";
|
|
10
|
+
export { aLostSocket } from "./lost.js";
|
|
11
|
+
export { history, observe } from "./observe.js";
|
|
12
|
+
export { signed } from "./signed.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,8EAA8E;AAE9E,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Camel, type Event, type EventData } from "../wire/codec.js";
|
|
2
|
+
import { type EventType } from "../wire/registry.js";
|
|
3
|
+
/** An event's data in camelCase. */
|
|
4
|
+
export type Payload<K extends EventType> = Camel<EventData<K>>;
|
|
5
|
+
/** A camelCase event, discriminated by `type`. */
|
|
6
|
+
export type CamelEvent = {
|
|
7
|
+
[K in EventType]: {
|
|
8
|
+
type: K;
|
|
9
|
+
data: Payload<K>;
|
|
10
|
+
};
|
|
11
|
+
}[EventType];
|
|
12
|
+
/** Listener for one event type. */
|
|
13
|
+
export type Listener<K extends EventType, Context> = (data: Payload<K>, context: Context) => unknown;
|
|
14
|
+
/** Listener for every event. */
|
|
15
|
+
export type AnyListener<Context> = (event: CamelEvent, context: Context) => unknown;
|
|
16
|
+
/** Convert a decoded event's data keys to camelCase. */
|
|
17
|
+
export declare function camelEvent(event: Event): CamelEvent;
|
|
18
|
+
/**
|
|
19
|
+
* The listeners of one agent or call. A throwing listener (sync or async) is reported to `onError`
|
|
20
|
+
* and the others still run.
|
|
21
|
+
*/
|
|
22
|
+
export declare class Listeners<Context> {
|
|
23
|
+
#private;
|
|
24
|
+
private readonly onError;
|
|
25
|
+
constructor(onError: (error: Error) => void);
|
|
26
|
+
/** Listen for one event type. The returned function stops listening. */
|
|
27
|
+
on<K extends EventType>(type: K, listener: Listener<K, Context>): () => void;
|
|
28
|
+
/** Listen for every event. The returned function stops listening. */
|
|
29
|
+
onAny(listener: AnyListener<Context>): () => void;
|
|
30
|
+
/** Dispatch to the type's listeners, then to the catch-all ones. */
|
|
31
|
+
emit(event: CamelEvent, context: Context): void;
|
|
32
|
+
}
|
|
33
|
+
/** Coerce any thrown value to an Error. */
|
|
34
|
+
export declare function asError(thrown: unknown): Error;
|
|
35
|
+
//# sourceMappingURL=listeners.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listeners.d.ts","sourceRoot":"","sources":["../../src/client/listeners.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAW,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,oCAAoC;AACpC,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,kDAAkD;AAClD,MAAM,MAAM,UAAU,GAAG;KAAG,CAAC,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE;CAAE,CAAC,SAAS,CAAC,CAAC;AAExF,mCAAmC;AACnC,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;AAErG,gCAAgC;AAChC,MAAM,MAAM,WAAW,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;AAIpF,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,CAEnD;AAED;;;GAGG;AACH,qBAAa,SAAS,CAAC,OAAO;;IAIhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAApC,YAA6B,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EAAI;IAEhE,wEAAwE;IACxE,EAAE,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,CAO3E;IAED,qEAAqE;IACrE,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAKhD;IAED,oEAAoE;IACpE,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAO9C;CAYF;AAED,2CAA2C;AAC3C,wBAAgB,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,CAE9C"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Event listener registry, shared by agents and calls.
|
|
2
|
+
import { toCamel } from "../wire/codec.js";
|
|
3
|
+
import {} from "../wire/registry.js";
|
|
4
|
+
/** Convert a decoded event's data keys to camelCase. */
|
|
5
|
+
export function camelEvent(event) {
|
|
6
|
+
return { type: event.type, data: toCamel(event.data) };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The listeners of one agent or call. A throwing listener (sync or async) is reported to `onError`
|
|
10
|
+
* and the others still run.
|
|
11
|
+
*/
|
|
12
|
+
export class Listeners {
|
|
13
|
+
onError;
|
|
14
|
+
#byType = new Map();
|
|
15
|
+
#any = new Set();
|
|
16
|
+
constructor(onError) {
|
|
17
|
+
this.onError = onError;
|
|
18
|
+
}
|
|
19
|
+
/** Listen for one event type. The returned function stops listening. */
|
|
20
|
+
on(type, listener) {
|
|
21
|
+
const listeners = this.#byType.get(type) ?? new Set();
|
|
22
|
+
this.#byType.set(type, listeners);
|
|
23
|
+
listeners.add(listener);
|
|
24
|
+
return () => {
|
|
25
|
+
listeners.delete(listener);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Listen for every event. The returned function stops listening. */
|
|
29
|
+
onAny(listener) {
|
|
30
|
+
this.#any.add(listener);
|
|
31
|
+
return () => {
|
|
32
|
+
this.#any.delete(listener);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Dispatch to the type's listeners, then to the catch-all ones. */
|
|
36
|
+
emit(event, context) {
|
|
37
|
+
for (const listener of this.#byType.get(event.type) ?? []) {
|
|
38
|
+
this.#run(() => listener(event.data, context));
|
|
39
|
+
}
|
|
40
|
+
for (const listener of this.#any) {
|
|
41
|
+
this.#run(() => listener(event, context));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
#run(listener) {
|
|
45
|
+
try {
|
|
46
|
+
const running = listener();
|
|
47
|
+
if (running instanceof Promise) {
|
|
48
|
+
running.catch((failed) => this.onError(asError(failed)));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (failed) {
|
|
52
|
+
this.onError(asError(failed));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Coerce any thrown value to an Error. */
|
|
57
|
+
export function asError(thrown) {
|
|
58
|
+
return thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=listeners.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listeners.js","sourceRoot":"","sources":["../../src/client/listeners.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAA0C,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAkB,MAAM,qBAAqB,CAAC;AAgBrD,wDAAwD;AACxD,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAgB,CAAC;AACvE,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,SAAS;IAIS,OAAO;IAH3B,OAAO,GAAG,IAAI,GAAG,EAAgC,CAAC;IAClD,IAAI,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEhD,YAA6B,OAA+B;uBAA/B,OAAO;IAA2B,CAAC;IAEhE,wEAAwE;IACxE,EAAE,CAAsB,IAAO,EAAE,QAA8B;QAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAmB,CAAC;QACvE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAClC,SAAS,CAAC,GAAG,CAAC,QAA2B,CAAC,CAAC;QAC3C,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,MAAM,CAAC,QAA2B,CAAC,CAAC;QAChD,CAAC,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,KAAK,CAAC,QAA8B;QAClC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,IAAI,CAAC,KAAiB,EAAE,OAAgB;QACtC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAE,QAAyD,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACnG,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACjC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,IAAI,CAAC,QAAuB;QAC1B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC;YAC3B,IAAI,OAAO,YAAY,OAAO,EAAE,CAAC;gBAC/B,OAAO,CAAC,KAAK,CAAC,CAAC,MAAe,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;CACF;AAED,2CAA2C;AAC3C,MAAM,UAAU,OAAO,CAAC,MAAe;IACrC,OAAO,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Whether an error is the gateway being out of reach, which the client redials on its own. */
|
|
2
|
+
/** Whether the error is an unreachable gateway (the client reconnects) rather than an app's own. */
|
|
3
|
+
export declare function aLostSocket(failed: Error): boolean;
|
|
4
|
+
//# sourceMappingURL=lost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lost.d.ts","sourceRoot":"","sources":["../../src/client/lost.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAK/F,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAElD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Whether an error is the gateway being out of reach, which the client redials on its own. */
|
|
2
|
+
// `ws` and network errors for an unreachable gateway, including a proxy answering during a restart.
|
|
3
|
+
const LOST = /^(Unexpected server response|WebSocket|socket hang up|connect |getaddrinfo |read ECONN|write E)/;
|
|
4
|
+
/** Whether the error is an unreachable gateway (the client reconnects) rather than an app's own. */
|
|
5
|
+
export function aLostSocket(failed) {
|
|
6
|
+
return typeof failed.code === "string" || LOST.test(failed.message);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=lost.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lost.js","sourceRoot":"","sources":["../../src/client/lost.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAE/F,oGAAoG;AACpG,MAAM,IAAI,GAAG,iGAAiG,CAAC;AAE/G,oGAAoG;AACpG,MAAM,UAAU,WAAW,CAAC,MAAa;IACvC,OAAO,OAAQ,MAAgC,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACjG,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type Entry } from "../wire/envelope.js";
|
|
2
|
+
import { type State } from "../wire/state.js";
|
|
3
|
+
import { type CamelEvent } from "./listeners.js";
|
|
4
|
+
import { type World } from "./signed.js";
|
|
5
|
+
/** A call's log or an agent's own log. */
|
|
6
|
+
export type LogTarget = {
|
|
7
|
+
call: string;
|
|
8
|
+
agent?: never;
|
|
9
|
+
} | {
|
|
10
|
+
agent: string;
|
|
11
|
+
call?: never;
|
|
12
|
+
};
|
|
13
|
+
/** Connection and cursor options for reading a log. */
|
|
14
|
+
export interface ReadOptions {
|
|
15
|
+
url: string;
|
|
16
|
+
apiKey: string;
|
|
17
|
+
/** The world to read from; omitted means sandbox. */
|
|
18
|
+
env?: World;
|
|
19
|
+
after?: number;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
}
|
|
22
|
+
/** One page of a log: entries after the cursor, their folded state, and the next cursor. */
|
|
23
|
+
export interface Page {
|
|
24
|
+
entries: Entry[];
|
|
25
|
+
state: State;
|
|
26
|
+
/** True while the log is still open and more entries will follow. */
|
|
27
|
+
live: boolean;
|
|
28
|
+
/** The cursor for the next page, or null when nothing follows. */
|
|
29
|
+
next: number | null;
|
|
30
|
+
}
|
|
31
|
+
/** One entry, its decoded event, and the folded state including it. */
|
|
32
|
+
export interface Observation {
|
|
33
|
+
entry: Entry;
|
|
34
|
+
event: CamelEvent;
|
|
35
|
+
state: State;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Stream a log from the cursor onward, until aborted.
|
|
39
|
+
*
|
|
40
|
+
* A dropped stream reconnects with `Last-Event-ID`; a reader that fell behind receives `log.gap`
|
|
41
|
+
* (with a snapshot when available) and then `log.caught_up`. Throws only if the first connection
|
|
42
|
+
* fails.
|
|
43
|
+
*/
|
|
44
|
+
export declare function observe(target: LogTarget, options: ReadOptions): AsyncGenerator<Observation>;
|
|
45
|
+
/**
|
|
46
|
+
* Read one page of a log after the cursor. Use `next` rather than the last entry's seq: an empty
|
|
47
|
+
* page of a live log still has one.
|
|
48
|
+
*/
|
|
49
|
+
export declare function history(target: LogTarget, options: ReadOptions): Promise<Page>;
|
|
50
|
+
//# sourceMappingURL=observe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/client/observe.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAU,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjD,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAE1F,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,GAAG,CAAC,EAAE,KAAK,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,4FAA4F;AAC5F,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,qEAAqE;IACrE,IAAI,EAAE,OAAO,CAAC;IACd,kEAAkE;IAClE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;CACd;AAKD;;;;;;GAMG;AACH,wBAAuB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,cAAc,CAAC,WAAW,CAAC,CA0BnG;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAcpF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Log reading: one URL served as a JSON page or SSE, folded by the protocol's reducer.
|
|
2
|
+
import { decodeEntry, eventOf } from "../wire/codec.js";
|
|
3
|
+
import {} from "../wire/envelope.js";
|
|
4
|
+
import { apply, initialState } from "../wire/reduce.js";
|
|
5
|
+
import {} from "../wire/state.js";
|
|
6
|
+
import { agentLogUrl, callLogUrl } from "./endpoints.js";
|
|
7
|
+
import { PinecallError } from "./frames.js";
|
|
8
|
+
import { camelEvent } from "./listeners.js";
|
|
9
|
+
import { signed } from "./signed.js";
|
|
10
|
+
const RETRY_MS = 1_000;
|
|
11
|
+
const RETRY_CAP_MS = 30_000;
|
|
12
|
+
/**
|
|
13
|
+
* Stream a log from the cursor onward, until aborted.
|
|
14
|
+
*
|
|
15
|
+
* A dropped stream reconnects with `Last-Event-ID`; a reader that fell behind receives `log.gap`
|
|
16
|
+
* (with a snapshot when available) and then `log.caught_up`. Throws only if the first connection
|
|
17
|
+
* fails.
|
|
18
|
+
*/
|
|
19
|
+
export async function* observe(target, options) {
|
|
20
|
+
let state = initialState();
|
|
21
|
+
let after = options.after ?? 0;
|
|
22
|
+
let attempt = 0;
|
|
23
|
+
let opened = false;
|
|
24
|
+
while (!aborted(options.signal)) {
|
|
25
|
+
try {
|
|
26
|
+
for await (const entry of stream(target, { ...options, after })) {
|
|
27
|
+
attempt = 0;
|
|
28
|
+
opened = true;
|
|
29
|
+
after = entry.seq;
|
|
30
|
+
state = apply(state, entry);
|
|
31
|
+
yield { entry, event: camelEvent(eventOf(entry)), state };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (failed) {
|
|
35
|
+
if (aborted(options.signal)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Never opened means misconfiguration (bad key, unknown call): surface it. Later drops retry.
|
|
39
|
+
if (!opened) {
|
|
40
|
+
throw failed;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
attempt += 1;
|
|
44
|
+
await pause(Math.min(RETRY_CAP_MS, RETRY_MS * 2 ** (attempt - 1)) * Math.random(), options.signal);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Read one page of a log after the cursor. Use `next` rather than the last entry's seq: an empty
|
|
49
|
+
* page of a live log still has one.
|
|
50
|
+
*/
|
|
51
|
+
export async function history(target, options) {
|
|
52
|
+
const response = await read(target, options, "application/json");
|
|
53
|
+
const page = await response.json();
|
|
54
|
+
if (page === null || typeof page !== "object" || !Array.isArray(page.entries)) {
|
|
55
|
+
throw new PinecallError("the log page is not { entries, live, next }");
|
|
56
|
+
}
|
|
57
|
+
const { entries: raw, live, next } = page;
|
|
58
|
+
const entries = raw.map((one) => decodeEntry(one));
|
|
59
|
+
return {
|
|
60
|
+
entries,
|
|
61
|
+
state: entries.reduce(apply, initialState()),
|
|
62
|
+
live: live === true,
|
|
63
|
+
next: typeof next === "number" ? next : null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** One SSE connection, yielding entries until it closes or is aborted. */
|
|
67
|
+
async function* stream(target, options) {
|
|
68
|
+
const response = await read(target, options, "text/event-stream");
|
|
69
|
+
if (response.body === null) {
|
|
70
|
+
throw new PinecallError("the event stream came back with no body");
|
|
71
|
+
}
|
|
72
|
+
let buffered = "";
|
|
73
|
+
const decoder = new TextDecoder();
|
|
74
|
+
for await (const chunk of response.body) {
|
|
75
|
+
buffered += decoder.decode(chunk, { stream: true });
|
|
76
|
+
let cut = buffered.indexOf("\n\n");
|
|
77
|
+
while (cut !== -1) {
|
|
78
|
+
const entry = entryIn(buffered.slice(0, cut));
|
|
79
|
+
buffered = buffered.slice(cut + 2);
|
|
80
|
+
cut = buffered.indexOf("\n\n");
|
|
81
|
+
if (entry !== null) {
|
|
82
|
+
yield entry;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Parse one SSE block, or null for a comment or keep-alive. */
|
|
88
|
+
function entryIn(block) {
|
|
89
|
+
const data = block
|
|
90
|
+
.split("\n")
|
|
91
|
+
.filter((line) => line.startsWith("data:"))
|
|
92
|
+
.map((line) => line.slice("data:".length).trimStart())
|
|
93
|
+
.join("\n");
|
|
94
|
+
return data === "" ? null : decodeEntry(JSON.parse(data));
|
|
95
|
+
}
|
|
96
|
+
async function read(target, options, accept) {
|
|
97
|
+
const url = new URL(target.call === undefined ? agentLogUrl(options.url, target.agent) : callLogUrl(options.url, target.call));
|
|
98
|
+
const after = options.after ?? 0;
|
|
99
|
+
if (after > 0) {
|
|
100
|
+
url.searchParams.set("after", String(after));
|
|
101
|
+
}
|
|
102
|
+
const headers = { accept, ...signed(options.apiKey, options.env) };
|
|
103
|
+
if (after > 0) {
|
|
104
|
+
headers["last-event-id"] = String(after);
|
|
105
|
+
}
|
|
106
|
+
const response = await fetch(url, { headers, ...(options.signal === undefined ? {} : { signal: options.signal }) });
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new PinecallError(`${url.pathname}: the gateway answered ${response.status}`);
|
|
109
|
+
}
|
|
110
|
+
return response;
|
|
111
|
+
}
|
|
112
|
+
// A function so TypeScript doesn't narrow `signal.aborted` across the loop.
|
|
113
|
+
function aborted(signal) {
|
|
114
|
+
return signal?.aborted === true;
|
|
115
|
+
}
|
|
116
|
+
async function pause(ms, signal) {
|
|
117
|
+
await new Promise((resolve) => {
|
|
118
|
+
const timer = setTimeout(resolve, ms);
|
|
119
|
+
signal?.addEventListener("abort", () => {
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
resolve();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=observe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observe.js","sourceRoot":"","sources":["../../src/client/observe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** One of the gateway's two worlds. */
|
|
2
|
+
export type World = "sandbox" | "production";
|
|
3
|
+
/**
|
|
4
|
+
* Header naming the world the request acts in. A person's key opens both worlds and this picks
|
|
5
|
+
* one (absent, the sandbox); a server token's world is fixed by its prefix, and the header may
|
|
6
|
+
* only agree.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ENV_HEADER = "pinecall-env";
|
|
9
|
+
/**
|
|
10
|
+
* Headers for one request or socket upgrade: the key as a Bearer (never in a URL, which lands in
|
|
11
|
+
* access logs) and the world if given. An empty key sends no authorization header.
|
|
12
|
+
*/
|
|
13
|
+
export declare function signed(apiKey: string, world?: World): Record<string, string>;
|
|
14
|
+
//# sourceMappingURL=signed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed.d.ts","sourceRoot":"","sources":["../../src/client/signed.ts"],"names":[],"mappings":"AAEA,uCAAuC;AACvC,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,YAAY,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,UAAU,iBAAiB,CAAC;AAEzC;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI5E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Request headers for the gateway: the API key and the world the request acts in.
|
|
2
|
+
/**
|
|
3
|
+
* Header naming the world the request acts in. A person's key opens both worlds and this picks
|
|
4
|
+
* one (absent, the sandbox); a server token's world is fixed by its prefix, and the header may
|
|
5
|
+
* only agree.
|
|
6
|
+
*/
|
|
7
|
+
export const ENV_HEADER = "pinecall-env";
|
|
8
|
+
/**
|
|
9
|
+
* Headers for one request or socket upgrade: the key as a Bearer (never in a URL, which lands in
|
|
10
|
+
* access logs) and the world if given. An empty key sends no authorization header.
|
|
11
|
+
*/
|
|
12
|
+
export function signed(apiKey, world) {
|
|
13
|
+
const headers = apiKey === "" ? {} : { authorization: `Bearer ${apiKey}` };
|
|
14
|
+
if (world !== undefined)
|
|
15
|
+
headers[ENV_HEADER] = world;
|
|
16
|
+
return headers;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=signed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed.js","sourceRoot":"","sources":["../../src/client/signed.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAKlF;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,MAAc,EAAE,KAAa;IAClD,MAAM,OAAO,GAA2B,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE,CAAC;IACnG,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;IACrD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Command, type Entry } from "../../wire/envelope.js";
|
|
2
|
+
import { type EventType } from "../../wire/registry.js";
|
|
3
|
+
/** Options for `FakeGateway.start()`. */
|
|
4
|
+
export interface FakeGatewayOptions {
|
|
5
|
+
/** The only accepted key; any other fails the handshake with 403. */
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
/** Slugs whose registration is refused, as when another agent or fleet holds them. */
|
|
8
|
+
taken?: string[];
|
|
9
|
+
/** Close every socket with 1008 and this reason, as for a key without the needed scope. */
|
|
10
|
+
refusesWith?: string;
|
|
11
|
+
/** Never answer `agent.drain`, like an older or hung gateway. */
|
|
12
|
+
holdsDrain?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** A command the fake gateway received. */
|
|
15
|
+
export type Received = Command;
|
|
16
|
+
/** A scripted search result chunk. */
|
|
17
|
+
export interface Scripted {
|
|
18
|
+
path: string;
|
|
19
|
+
heading: string | null;
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
/** A search request recorded by the fake gateway. */
|
|
23
|
+
export interface Searched {
|
|
24
|
+
call: string;
|
|
25
|
+
query: string;
|
|
26
|
+
k: number | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A fake gateway with no sessions, store or model. It numbers entries, answers the commands the
|
|
30
|
+
* app socket handles itself, and lets a test push any entry to connected apps.
|
|
31
|
+
*/
|
|
32
|
+
export declare class FakeGateway {
|
|
33
|
+
#private;
|
|
34
|
+
private readonly options;
|
|
35
|
+
readonly received: Received[];
|
|
36
|
+
/** Every search made through this gateway, in order. */
|
|
37
|
+
readonly searched: Searched[];
|
|
38
|
+
private constructor();
|
|
39
|
+
/** Start on a random port. Close it when the test is over. */
|
|
40
|
+
static start(options?: FakeGatewayOptions): Promise<FakeGateway>;
|
|
41
|
+
/** The URL to give a client as PINECALL_URL. */
|
|
42
|
+
get url(): string;
|
|
43
|
+
/** How many apps are connected right now. */
|
|
44
|
+
get connections(): number;
|
|
45
|
+
/** Received commands of one type, in arrival order. */
|
|
46
|
+
commandsOf(type: string): Received[];
|
|
47
|
+
/** Push one numbered entry to every connected app. */
|
|
48
|
+
emit(agent: string, call: string | null, type: EventType, data: Record<string, unknown>): Entry;
|
|
49
|
+
/** Set the chunks (best first) that subsequent searches return. */
|
|
50
|
+
finds(chunks: Scripted[]): void;
|
|
51
|
+
/** Drop every open socket while still listening, simulating a gateway restart. */
|
|
52
|
+
cut(): void;
|
|
53
|
+
/** Stop every connected app as `POST /v1/apps/{app}/stop` does: an `error` coded `stopped`, then close. */
|
|
54
|
+
stop(why: string): void;
|
|
55
|
+
/** Stop listening and close all connections. */
|
|
56
|
+
close(): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../../src/client/testing/gateway.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAiB,KAAK,KAAK,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAW1E,yCAAyC;AACzC,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,2CAA2C;AAC3C,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC;AAE/B,sCAAsC;AACtC,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qDAAqD;AACrD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvB;AAKD;;;GAGG;AACH,qBAAa,WAAW;;IAaqC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZnF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAM;IACnC,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAM;IAUnC,OAAO,eAIN;IAGD,8DAA8D;IAC9D,OAAa,KAAK,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBzE;IAED,gDAAgD;IAChD,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,6CAA6C;IAC7C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,uDAAuD;IACvD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAEnC;IAED,sDAAsD;IACtD,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAM9F;IAED,mEAAmE;IACnE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAE9B;IAED,kFAAkF;IAClF,GAAG,IAAI,IAAI,CAKV;IAED,2GAA2G;IAC3G,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAKtB;IAED,gDAAgD;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3B;CA0EF"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// An in-process fake gateway: enough of the app socket to test an app's agents.
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { CommandSchema } from "../../wire/envelope.js";
|
|
4
|
+
import { EPHEMERAL_EVENTS } from "../../wire/registry.js";
|
|
5
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
6
|
+
import { LOOPBACK } from "./loopback.js";
|
|
7
|
+
// Unknown keys are refused during the handshake, so the upgrade never happens.
|
|
8
|
+
const FORBIDDEN = 403;
|
|
9
|
+
// Close code for a valid key that lacks the scope for this endpoint.
|
|
10
|
+
const POLICY_VIOLATION = 1008;
|
|
11
|
+
// The only HTTP endpoint: knowledge search, answered with scripted chunks.
|
|
12
|
+
const LOOKUP = /^\/v1\/calls\/([^/]+)\/lookup$/;
|
|
13
|
+
/**
|
|
14
|
+
* A fake gateway with no sessions, store or model. It numbers entries, answers the commands the
|
|
15
|
+
* app socket handles itself, and lets a test push any entry to connected apps.
|
|
16
|
+
*/
|
|
17
|
+
export class FakeGateway {
|
|
18
|
+
options;
|
|
19
|
+
received = [];
|
|
20
|
+
/** Every search made through this gateway, in order. */
|
|
21
|
+
searched = [];
|
|
22
|
+
#http;
|
|
23
|
+
#server;
|
|
24
|
+
#found = [];
|
|
25
|
+
#sockets = new Set();
|
|
26
|
+
#refused;
|
|
27
|
+
#seq = 0;
|
|
28
|
+
#apps = 0;
|
|
29
|
+
#port = 0;
|
|
30
|
+
constructor(http, server, options) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.#http = http;
|
|
33
|
+
this.#server = server;
|
|
34
|
+
this.#refused = new Set(options.taken ?? []);
|
|
35
|
+
}
|
|
36
|
+
// The key is checked during the handshake, as the real gateway does.
|
|
37
|
+
/** Start on a random port. Close it when the test is over. */
|
|
38
|
+
static async start(options = {}) {
|
|
39
|
+
// One HTTP server for both the /v1/apps upgrade and the search endpoint.
|
|
40
|
+
const http = createServer((request, response) => void gateway.#answerHttp(request, response));
|
|
41
|
+
const server = new WebSocketServer({
|
|
42
|
+
server: http,
|
|
43
|
+
path: "/v1/apps",
|
|
44
|
+
verifyClient: ({ req }, done) => done(options.apiKey === undefined || req.headers.authorization === `Bearer ${options.apiKey}`, FORBIDDEN),
|
|
45
|
+
});
|
|
46
|
+
const gateway = new FakeGateway(http, server, options);
|
|
47
|
+
// Bind to 127.0.0.1 only: the IPv6 wildcard can be granted on a port another program holds
|
|
48
|
+
// on IPv4, and the client's 127.0.0.1 connection would reach that program instead.
|
|
49
|
+
await new Promise((resolve) => http.listen(0, LOOPBACK, resolve));
|
|
50
|
+
const address = http.address();
|
|
51
|
+
gateway.#port = typeof address === "object" && address !== null ? address.port : 0;
|
|
52
|
+
server.on("connection", (socket) => {
|
|
53
|
+
if (options.refusesWith !== undefined) {
|
|
54
|
+
socket.close(POLICY_VIOLATION, options.refusesWith);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
gateway.#accept(socket);
|
|
58
|
+
});
|
|
59
|
+
return gateway;
|
|
60
|
+
}
|
|
61
|
+
/** The URL to give a client as PINECALL_URL. */
|
|
62
|
+
get url() {
|
|
63
|
+
return `http://${LOOPBACK}:${this.#port}`;
|
|
64
|
+
}
|
|
65
|
+
/** How many apps are connected right now. */
|
|
66
|
+
get connections() {
|
|
67
|
+
return this.#sockets.size;
|
|
68
|
+
}
|
|
69
|
+
/** Received commands of one type, in arrival order. */
|
|
70
|
+
commandsOf(type) {
|
|
71
|
+
return this.received.filter((command) => command.type === type);
|
|
72
|
+
}
|
|
73
|
+
/** Push one numbered entry to every connected app. */
|
|
74
|
+
emit(agent, call, type, data) {
|
|
75
|
+
const entry = { seq: (this.#seq += 1), ts: Date.now() / 1000, call, agent, type, ephemeral: EPHEMERAL_EVENTS.has(type), data };
|
|
76
|
+
for (const socket of this.#sockets) {
|
|
77
|
+
socket.send(JSON.stringify(entry));
|
|
78
|
+
}
|
|
79
|
+
return entry;
|
|
80
|
+
}
|
|
81
|
+
/** Set the chunks (best first) that subsequent searches return. */
|
|
82
|
+
finds(chunks) {
|
|
83
|
+
this.#found = chunks;
|
|
84
|
+
}
|
|
85
|
+
/** Drop every open socket while still listening, simulating a gateway restart. */
|
|
86
|
+
cut() {
|
|
87
|
+
for (const socket of this.#sockets) {
|
|
88
|
+
socket.terminate();
|
|
89
|
+
}
|
|
90
|
+
this.#sockets.clear();
|
|
91
|
+
}
|
|
92
|
+
/** Stop every connected app as `POST /v1/apps/{app}/stop` does: an `error` coded `stopped`, then close. */
|
|
93
|
+
stop(why) {
|
|
94
|
+
for (const socket of this.#sockets) {
|
|
95
|
+
socket.send(JSON.stringify(this.#entry("", null, "error", { code: "stopped", message: why, recoverable: false })));
|
|
96
|
+
socket.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Stop listening and close all connections. */
|
|
100
|
+
async close() {
|
|
101
|
+
this.cut();
|
|
102
|
+
await new Promise((resolve, reject) => this.#server.close((failed) => (failed ? reject(failed) : resolve())));
|
|
103
|
+
this.#http.closeAllConnections();
|
|
104
|
+
await new Promise((resolve, reject) => this.#http.close((failed) => (failed ? reject(failed) : resolve())));
|
|
105
|
+
}
|
|
106
|
+
async #answerHttp(request, response) {
|
|
107
|
+
const call = LOOKUP.exec(request.url ?? "")?.[1];
|
|
108
|
+
if (request.method !== "POST" || call === undefined) {
|
|
109
|
+
response.writeHead(404).end();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (this.options.apiKey !== undefined && request.headers.authorization !== `Bearer ${this.options.apiKey}`) {
|
|
113
|
+
response.writeHead(401, { "content-type": "application/json" }).end(JSON.stringify({ detail: "this door takes an API key" }));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const chunks = [];
|
|
117
|
+
for await (const chunk of request)
|
|
118
|
+
chunks.push(chunk);
|
|
119
|
+
const { input } = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
120
|
+
this.searched.push({ call: decodeURIComponent(call), query: input.query, k: input.k });
|
|
121
|
+
const found = input.k === undefined ? this.#found : this.#found.slice(0, input.k);
|
|
122
|
+
response.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ output: { chunks: found }, took_ms: 1 }));
|
|
123
|
+
}
|
|
124
|
+
#accept(socket) {
|
|
125
|
+
this.#sockets.add(socket);
|
|
126
|
+
// Per-connection id, returned in `agent.registered` as the real gateway does.
|
|
127
|
+
const app = `app_${(this.#apps += 1)}`;
|
|
128
|
+
socket.on("message", (raw) => this.#take(socket, app, raw));
|
|
129
|
+
socket.on("close", () => this.#sockets.delete(socket));
|
|
130
|
+
// An unhandled `error` event throws in Node; `ws` emits one for any unparseable frame, which
|
|
131
|
+
// would crash the whole test run.
|
|
132
|
+
socket.on("error", () => this.#sockets.delete(socket));
|
|
133
|
+
}
|
|
134
|
+
#take(socket, app, raw) {
|
|
135
|
+
const command = CommandSchema.parse(JSON.parse(raw.toString()));
|
|
136
|
+
this.received.push(command);
|
|
137
|
+
switch (command.type) {
|
|
138
|
+
case "agent.register":
|
|
139
|
+
return this.#register(socket, app, command);
|
|
140
|
+
case "agent.configure":
|
|
141
|
+
return this.#answer(socket, command, "agent.configured", { changed: Object.keys((command.data["config"] ?? {})) });
|
|
142
|
+
case "ping":
|
|
143
|
+
return this.#answer(socket, command, "pong", { ts: Date.now() / 1000 });
|
|
144
|
+
case "agent.drain":
|
|
145
|
+
if (this.options.holdsDrain === true)
|
|
146
|
+
return;
|
|
147
|
+
return this.#answer(socket, command, "agent.draining", { app, env: "sandbox", handed: 0, parked: 0 });
|
|
148
|
+
default:
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Several sockets may register one agent; only slugs in `taken` are refused.
|
|
153
|
+
#register(socket, app, command) {
|
|
154
|
+
if (this.#refused.has(command.agent)) {
|
|
155
|
+
this.#refuse(socket, command, "refused", `${command.agent} answers at a door somebody else has`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const { routes, sdk } = command.data;
|
|
159
|
+
this.#answer(socket, command, "agent.registered", sdk === undefined ? { routes, app } : { routes, app, sdk });
|
|
160
|
+
}
|
|
161
|
+
#answer(socket, command, type, data) {
|
|
162
|
+
socket.send(JSON.stringify(this.#entry(command.agent, null, type, data)));
|
|
163
|
+
}
|
|
164
|
+
#refuse(socket, command, code, message) {
|
|
165
|
+
const data = { code, message, recoverable: true, command: command.type };
|
|
166
|
+
if (command.id !== undefined) {
|
|
167
|
+
data["id"] = command.id;
|
|
168
|
+
}
|
|
169
|
+
socket.send(JSON.stringify(this.#entry(command.agent, null, "error", data)));
|
|
170
|
+
}
|
|
171
|
+
#entry(agent, call, type, data) {
|
|
172
|
+
return { seq: (this.#seq += 1), ts: Date.now() / 1000, call, agent, type, ephemeral: EPHEMERAL_EVENTS.has(type), data };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=gateway.js.map
|