@zocomputer/agent-sdk 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +673 -0
  3. package/dist/attachments.js +52 -0
  4. package/dist/gateway-fetch.js +67 -0
  5. package/dist/index.js +4169 -0
  6. package/dist/initiator-auth.js +49 -0
  7. package/dist/platform/agent-sandbox/index.js +691 -0
  8. package/dist/platform/cloud-tools/image.js +498 -0
  9. package/dist/platform/cloud-tools/index.js +507 -0
  10. package/dist/platform/cloud-tools/web-search.js +87 -0
  11. package/dist/platform/runtime-ai/gateway.js +87 -0
  12. package/dist/platform/runtime-ai/index.js +91 -0
  13. package/dist/platform/runtime-ai/register.js +88 -0
  14. package/dist/platform/runtime-ai/session-fetch.js +54 -0
  15. package/dist/platform/runtime-auth/index.js +141 -0
  16. package/dist/state-files.js +287 -0
  17. package/dist/state-sandbox.js +383 -0
  18. package/dist/state.js +29 -0
  19. package/dist/steer-inbox.js +84 -0
  20. package/dist/steer.js +83 -0
  21. package/package.json +143 -0
  22. package/platform/agent-sandbox/api-client.ts +196 -0
  23. package/platform/agent-sandbox/index.ts +27 -0
  24. package/platform/agent-sandbox/pure.ts +83 -0
  25. package/platform/agent-sandbox/sftp.ts +141 -0
  26. package/platform/agent-sandbox/ssh-connection.ts +165 -0
  27. package/platform/agent-sandbox/ssh-exec.ts +98 -0
  28. package/platform/agent-sandbox/ssh-session.ts +487 -0
  29. package/platform/agent-sandbox/zo-backend.ts +88 -0
  30. package/platform/agent-sandbox/zo-sandbox.ts +39 -0
  31. package/platform/cloud-tools/image-path.ts +44 -0
  32. package/platform/cloud-tools/image.ts +225 -0
  33. package/platform/cloud-tools/index.ts +22 -0
  34. package/platform/cloud-tools/state-files.ts +368 -0
  35. package/platform/cloud-tools/tool-meta.ts +32 -0
  36. package/platform/cloud-tools/web-search.ts +15 -0
  37. package/platform/runtime-ai/gateway.ts +76 -0
  38. package/platform/runtime-ai/index.ts +20 -0
  39. package/platform/runtime-ai/register.ts +26 -0
  40. package/platform/runtime-ai/session-fetch.ts +124 -0
  41. package/platform/runtime-auth/index.ts +331 -0
  42. package/src/async-tasks.ts +273 -0
  43. package/src/attachments.ts +109 -0
  44. package/src/backgroundable.ts +88 -0
  45. package/src/bounded-output.ts +159 -0
  46. package/src/dir-conventions.ts +238 -0
  47. package/src/extract/cache.ts +40 -0
  48. package/src/extract/docx.ts +18 -0
  49. package/src/extract/pdf.ts +54 -0
  50. package/src/extract/sheet.ts +56 -0
  51. package/src/file-kind.ts +258 -0
  52. package/src/file-view.ts +80 -0
  53. package/src/gateway-fetch.ts +115 -0
  54. package/src/glob-match.ts +13 -0
  55. package/src/hooks.ts +213 -0
  56. package/src/index.ts +419 -0
  57. package/src/initiator-auth.ts +81 -0
  58. package/src/instructions.ts +224 -0
  59. package/src/list-files.ts +41 -0
  60. package/src/mock-model.ts +572 -0
  61. package/src/park-delivery.ts +247 -0
  62. package/src/path-locks.ts +52 -0
  63. package/src/read-file-content.ts +142 -0
  64. package/src/read-text.ts +40 -0
  65. package/src/redeliver.ts +155 -0
  66. package/src/run.ts +159 -0
  67. package/src/sandbox-io.ts +414 -0
  68. package/src/state-files.ts +460 -0
  69. package/src/state-sandbox.ts +710 -0
  70. package/src/state.ts +96 -0
  71. package/src/steer-inbox.ts +105 -0
  72. package/src/steer-tool.ts +83 -0
  73. package/src/steer.ts +146 -0
  74. package/src/task.ts +320 -0
  75. package/src/tools/bash.ts +143 -0
  76. package/src/tools/edit.ts +58 -0
  77. package/src/tools/glob.ts +56 -0
  78. package/src/tools/grep.ts +188 -0
  79. package/src/tools/read.ts +319 -0
  80. package/src/tools/tasks.ts +241 -0
  81. package/src/tools/webfetch.ts +368 -0
  82. package/src/tools/write.ts +42 -0
  83. package/src/walk.ts +112 -0
  84. package/src/watch-output.ts +104 -0
  85. package/src/web-fetch.ts +324 -0
  86. package/src/web-page.ts +179 -0
  87. package/src/workspace-io.ts +225 -0
  88. package/src/workspace.ts +41 -0
@@ -0,0 +1,91 @@
1
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/gateway.ts
2
+ import { createGateway } from "ai";
3
+
4
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/session-fetch.ts
5
+ var EVE_SESSION_HEADER = "x-zo-eve-session";
6
+ var EVE_TURN_HEADER = "x-zo-eve-turn";
7
+ var EVE_CONTEXT_STORAGE_KEY = Symbol.for("eve.context-storage");
8
+ var SESSION_ID_KEY_NAME = "eve.sessionId";
9
+ var SESSION_KEY_NAME = "eve.session";
10
+ function hasMethod(value, name) {
11
+ return typeof value === "object" && value !== null && typeof value[name] === "function";
12
+ }
13
+ function ambientEveSessionId() {
14
+ const value = ambientContextValue(SESSION_ID_KEY_NAME);
15
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
16
+ }
17
+ function ambientEveTurnId() {
18
+ const session = ambientContextValue(SESSION_KEY_NAME);
19
+ if (typeof session !== "object" || session === null)
20
+ return;
21
+ const turn = session["turn"];
22
+ if (typeof turn !== "object" || turn === null)
23
+ return;
24
+ const id = turn["id"];
25
+ return typeof id === "string" && id.trim().length > 0 ? id : undefined;
26
+ }
27
+ function ambientContextValue(keyName) {
28
+ const storage = Reflect.get(globalThis, EVE_CONTEXT_STORAGE_KEY);
29
+ if (!hasMethod(storage, "getStore"))
30
+ return;
31
+ const store = storage.getStore();
32
+ if (!hasMethod(store, "get"))
33
+ return;
34
+ return store.get({ name: keyName });
35
+ }
36
+ function eveSessionFetch(getSessionId = ambientEveSessionId, baseFetch = globalThis.fetch, getTurnId = ambientEveTurnId) {
37
+ return Object.assign((input, init) => {
38
+ const sessionId = getSessionId()?.trim();
39
+ if (!sessionId)
40
+ return baseFetch(input, init);
41
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
42
+ headers.set(EVE_SESSION_HEADER, sessionId);
43
+ const turnId = getTurnId()?.trim();
44
+ if (turnId)
45
+ headers.set(EVE_TURN_HEADER, turnId);
46
+ else
47
+ headers.delete(EVE_TURN_HEADER);
48
+ return baseFetch(input, { ...init, headers });
49
+ }, baseFetch);
50
+ }
51
+
52
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/gateway.ts
53
+ var ZO_TOOL_HEADER = "x-zo-tool";
54
+ var DEFAULT_ZO_AI_BASE_URL = "http://localhost:4000/runtime/ai/v4/ai";
55
+ var DEFAULT_ZO_AI_KEY = "dev-proxy";
56
+ var AGENT_TOKEN_HEADER = "x-zo-agent-token";
57
+ var AGENT_TOKEN_ENV = "ZO_AGENT_TOKEN";
58
+ function agentAuthHeaders(token = process.env[AGENT_TOKEN_ENV]) {
59
+ const trimmed = token?.trim();
60
+ return trimmed ? { [AGENT_TOKEN_HEADER]: trimmed } : {};
61
+ }
62
+ function resolveZoGatewayBaseUrl(baseURL = process.env.ZO_AI_BASE_URL) {
63
+ const trimmed = baseURL?.trim();
64
+ return trimmed && trimmed.length > 0 ? trimmed : DEFAULT_ZO_AI_BASE_URL;
65
+ }
66
+ function resolveZoGatewayApiKey(apiKey = process.env.ZO_AI_KEY) {
67
+ const trimmed = apiKey?.trim();
68
+ return trimmed && trimmed.length > 0 ? trimmed : DEFAULT_ZO_AI_KEY;
69
+ }
70
+ function zoGateway(options = {}) {
71
+ return createGateway({
72
+ ...options,
73
+ headers: { ...agentAuthHeaders(), ...options.headers },
74
+ apiKey: resolveZoGatewayApiKey(options.apiKey),
75
+ baseURL: resolveZoGatewayBaseUrl(options.baseURL),
76
+ fetch: eveSessionFetch(undefined, options.fetch)
77
+ });
78
+ }
79
+ export {
80
+ zoGateway,
81
+ resolveZoGatewayBaseUrl,
82
+ resolveZoGatewayApiKey,
83
+ eveSessionFetch,
84
+ ambientEveTurnId,
85
+ ambientEveSessionId,
86
+ ZO_TOOL_HEADER,
87
+ EVE_TURN_HEADER,
88
+ EVE_SESSION_HEADER,
89
+ DEFAULT_ZO_AI_KEY,
90
+ DEFAULT_ZO_AI_BASE_URL
91
+ };
@@ -0,0 +1,88 @@
1
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/gateway.ts
2
+ import { createGateway } from "ai";
3
+
4
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/session-fetch.ts
5
+ var EVE_SESSION_HEADER = "x-zo-eve-session";
6
+ var EVE_TURN_HEADER = "x-zo-eve-turn";
7
+ var EVE_CONTEXT_STORAGE_KEY = Symbol.for("eve.context-storage");
8
+ var SESSION_ID_KEY_NAME = "eve.sessionId";
9
+ var SESSION_KEY_NAME = "eve.session";
10
+ function hasMethod(value, name) {
11
+ return typeof value === "object" && value !== null && typeof value[name] === "function";
12
+ }
13
+ function ambientEveSessionId() {
14
+ const value = ambientContextValue(SESSION_ID_KEY_NAME);
15
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
16
+ }
17
+ function ambientEveTurnId() {
18
+ const session = ambientContextValue(SESSION_KEY_NAME);
19
+ if (typeof session !== "object" || session === null)
20
+ return;
21
+ const turn = session["turn"];
22
+ if (typeof turn !== "object" || turn === null)
23
+ return;
24
+ const id = turn["id"];
25
+ return typeof id === "string" && id.trim().length > 0 ? id : undefined;
26
+ }
27
+ function ambientContextValue(keyName) {
28
+ const storage = Reflect.get(globalThis, EVE_CONTEXT_STORAGE_KEY);
29
+ if (!hasMethod(storage, "getStore"))
30
+ return;
31
+ const store = storage.getStore();
32
+ if (!hasMethod(store, "get"))
33
+ return;
34
+ return store.get({ name: keyName });
35
+ }
36
+ function eveSessionFetch(getSessionId = ambientEveSessionId, baseFetch = globalThis.fetch, getTurnId = ambientEveTurnId) {
37
+ return Object.assign((input, init) => {
38
+ const sessionId = getSessionId()?.trim();
39
+ if (!sessionId)
40
+ return baseFetch(input, init);
41
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
42
+ headers.set(EVE_SESSION_HEADER, sessionId);
43
+ const turnId = getTurnId()?.trim();
44
+ if (turnId)
45
+ headers.set(EVE_TURN_HEADER, turnId);
46
+ else
47
+ headers.delete(EVE_TURN_HEADER);
48
+ return baseFetch(input, { ...init, headers });
49
+ }, baseFetch);
50
+ }
51
+
52
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/gateway.ts
53
+ var DEFAULT_ZO_AI_BASE_URL = "http://localhost:4000/runtime/ai/v4/ai";
54
+ var DEFAULT_ZO_AI_KEY = "dev-proxy";
55
+ var AGENT_TOKEN_HEADER = "x-zo-agent-token";
56
+ var AGENT_TOKEN_ENV = "ZO_AGENT_TOKEN";
57
+ function agentAuthHeaders(token = process.env[AGENT_TOKEN_ENV]) {
58
+ const trimmed = token?.trim();
59
+ return trimmed ? { [AGENT_TOKEN_HEADER]: trimmed } : {};
60
+ }
61
+ function resolveZoGatewayBaseUrl(baseURL = process.env.ZO_AI_BASE_URL) {
62
+ const trimmed = baseURL?.trim();
63
+ return trimmed && trimmed.length > 0 ? trimmed : DEFAULT_ZO_AI_BASE_URL;
64
+ }
65
+ function resolveZoGatewayApiKey(apiKey = process.env.ZO_AI_KEY) {
66
+ const trimmed = apiKey?.trim();
67
+ return trimmed && trimmed.length > 0 ? trimmed : DEFAULT_ZO_AI_KEY;
68
+ }
69
+ function zoGateway(options = {}) {
70
+ return createGateway({
71
+ ...options,
72
+ headers: { ...agentAuthHeaders(), ...options.headers },
73
+ apiKey: resolveZoGatewayApiKey(options.apiKey),
74
+ baseURL: resolveZoGatewayBaseUrl(options.baseURL),
75
+ fetch: eveSessionFetch(undefined, options.fetch)
76
+ });
77
+ }
78
+
79
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/register.ts
80
+ var SLOT = "AI_SDK_DEFAULT_PROVIDER";
81
+ if (!(SLOT in globalThis)) {
82
+ Object.defineProperty(globalThis, SLOT, {
83
+ value: zoGateway(),
84
+ writable: false,
85
+ configurable: false,
86
+ enumerable: false
87
+ });
88
+ }
@@ -0,0 +1,54 @@
1
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-ai/session-fetch.ts
2
+ var EVE_SESSION_HEADER = "x-zo-eve-session";
3
+ var EVE_TURN_HEADER = "x-zo-eve-turn";
4
+ var EVE_CONTEXT_STORAGE_KEY = Symbol.for("eve.context-storage");
5
+ var SESSION_ID_KEY_NAME = "eve.sessionId";
6
+ var SESSION_KEY_NAME = "eve.session";
7
+ function hasMethod(value, name) {
8
+ return typeof value === "object" && value !== null && typeof value[name] === "function";
9
+ }
10
+ function ambientEveSessionId() {
11
+ const value = ambientContextValue(SESSION_ID_KEY_NAME);
12
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
13
+ }
14
+ function ambientEveTurnId() {
15
+ const session = ambientContextValue(SESSION_KEY_NAME);
16
+ if (typeof session !== "object" || session === null)
17
+ return;
18
+ const turn = session["turn"];
19
+ if (typeof turn !== "object" || turn === null)
20
+ return;
21
+ const id = turn["id"];
22
+ return typeof id === "string" && id.trim().length > 0 ? id : undefined;
23
+ }
24
+ function ambientContextValue(keyName) {
25
+ const storage = Reflect.get(globalThis, EVE_CONTEXT_STORAGE_KEY);
26
+ if (!hasMethod(storage, "getStore"))
27
+ return;
28
+ const store = storage.getStore();
29
+ if (!hasMethod(store, "get"))
30
+ return;
31
+ return store.get({ name: keyName });
32
+ }
33
+ function eveSessionFetch(getSessionId = ambientEveSessionId, baseFetch = globalThis.fetch, getTurnId = ambientEveTurnId) {
34
+ return Object.assign((input, init) => {
35
+ const sessionId = getSessionId()?.trim();
36
+ if (!sessionId)
37
+ return baseFetch(input, init);
38
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
39
+ headers.set(EVE_SESSION_HEADER, sessionId);
40
+ const turnId = getTurnId()?.trim();
41
+ if (turnId)
42
+ headers.set(EVE_TURN_HEADER, turnId);
43
+ else
44
+ headers.delete(EVE_TURN_HEADER);
45
+ return baseFetch(input, { ...init, headers });
46
+ }, baseFetch);
47
+ }
48
+ export {
49
+ eveSessionFetch,
50
+ ambientEveTurnId,
51
+ ambientEveSessionId,
52
+ EVE_TURN_HEADER,
53
+ EVE_SESSION_HEADER
54
+ };
@@ -0,0 +1,141 @@
1
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/platform/runtime-auth/index.ts
2
+ import { SignJWT, jwtVerify } from "jose";
3
+ var AGENT_TOKEN_HEADER = "x-zo-agent-token";
4
+ var EVE_SESSION_HEADER = "x-zo-eve-session";
5
+ var EVE_TURN_HEADER = "x-zo-eve-turn";
6
+ var AGENT_TOKEN_ENV = "ZO_AGENT_TOKEN";
7
+ var ZO_PLATFORM_ORG = {
8
+ id: "org_zo",
9
+ name: "Zo",
10
+ slug: "zo"
11
+ };
12
+ var BUILDER_AGENT_IDENTITY = {
13
+ agentProjectId: "agt_builder",
14
+ ownerOrgId: ZO_PLATFORM_ORG.id
15
+ };
16
+ var LOCAL_AGENT_IDENTITY = {
17
+ agentProjectId: "agt_local",
18
+ ownerOrgId: ZO_PLATFORM_ORG.id
19
+ };
20
+ var RESERVED_AGENT_PROJECT_IDS = [
21
+ BUILDER_AGENT_IDENTITY.agentProjectId,
22
+ LOCAL_AGENT_IDENTITY.agentProjectId
23
+ ];
24
+ var ISSUER = "zo-api";
25
+ var AGENT_TOKEN_TYP = "zo-agent";
26
+ var defaultClock = () => Math.floor(Date.now() / 1000);
27
+ function key(secret) {
28
+ return new TextEncoder().encode(secret);
29
+ }
30
+ function asString(v) {
31
+ return typeof v === "string" && v.length > 0 ? v : undefined;
32
+ }
33
+ async function mintAgentToken(input) {
34
+ const now = (input.clock ?? defaultClock)();
35
+ const payload = {
36
+ typ: AGENT_TOKEN_TYP,
37
+ agentProjectId: input.claims.agentProjectId,
38
+ ownerOrgId: input.claims.ownerOrgId,
39
+ ...input.claims.deploymentId ? { deploymentId: input.claims.deploymentId } : {}
40
+ };
41
+ return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuer(ISSUER).setIssuedAt(now).setExpirationTime(now + input.ttlSeconds).sign(key(input.secret));
42
+ }
43
+ async function verifyAgentToken(token, secret, clock = defaultClock) {
44
+ try {
45
+ const { payload } = await jwtVerify(token, key(secret), {
46
+ issuer: ISSUER,
47
+ currentDate: new Date(clock() * 1000)
48
+ });
49
+ if (payload.typ !== AGENT_TOKEN_TYP)
50
+ return null;
51
+ const agentProjectId = asString(payload.agentProjectId);
52
+ const ownerOrgId = asString(payload.ownerOrgId);
53
+ if (!agentProjectId || !ownerOrgId)
54
+ return null;
55
+ const deploymentId = asString(payload.deploymentId);
56
+ return { agentProjectId, ownerOrgId, ...deploymentId ? { deploymentId } : {} };
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+ async function resolveAgentContext(agentToken, secret, eveSessionId, clock = defaultClock) {
62
+ const claims = await verifyAgentToken(agentToken, secret, clock);
63
+ if (!claims)
64
+ return null;
65
+ return {
66
+ actor: {
67
+ kind: "agent",
68
+ agentProjectId: claims.agentProjectId,
69
+ ownerOrgId: claims.ownerOrgId,
70
+ ...claims.deploymentId ? { deploymentId: claims.deploymentId } : {}
71
+ },
72
+ ...eveSessionId ? { eveSessionId } : {}
73
+ };
74
+ }
75
+ var IDENTITY_BEARER_TYP = "zo-identity";
76
+ async function mintIdentityBearer(input) {
77
+ const now = (input.clock ?? defaultClock)();
78
+ const payload = {
79
+ typ: IDENTITY_BEARER_TYP,
80
+ userId: input.claims.userId,
81
+ agentId: input.claims.agentId
82
+ };
83
+ return new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuer(ISSUER).setIssuedAt(now).setExpirationTime(now + input.ttlSeconds).sign(key(input.secret));
84
+ }
85
+ async function verifyIdentityBearer(bearer, secret, clock = defaultClock) {
86
+ try {
87
+ const { payload } = await jwtVerify(bearer, key(secret), {
88
+ issuer: ISSUER,
89
+ currentDate: new Date(clock() * 1000)
90
+ });
91
+ if (payload.typ !== IDENTITY_BEARER_TYP)
92
+ return null;
93
+ const userId = asString(payload.userId);
94
+ const agentId = asString(payload.agentId);
95
+ if (!userId || !agentId)
96
+ return null;
97
+ return { userId, agentId };
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+ var INITIATOR_HEADER = "x-zo-initiator";
103
+ function formatInitiator(identity) {
104
+ return JSON.stringify({ userId: identity.userId, agentId: identity.agentId });
105
+ }
106
+ function parseInitiator(value) {
107
+ if (!value)
108
+ return null;
109
+ let parsed;
110
+ try {
111
+ parsed = JSON.parse(value);
112
+ } catch {
113
+ return null;
114
+ }
115
+ if (typeof parsed !== "object" || parsed === null)
116
+ return null;
117
+ const { userId, agentId } = parsed;
118
+ if (typeof userId !== "string" || !userId)
119
+ return null;
120
+ if (typeof agentId !== "string" || !agentId)
121
+ return null;
122
+ return { userId, agentId };
123
+ }
124
+ export {
125
+ verifyIdentityBearer,
126
+ verifyAgentToken,
127
+ resolveAgentContext,
128
+ parseInitiator,
129
+ mintIdentityBearer,
130
+ mintAgentToken,
131
+ formatInitiator,
132
+ ZO_PLATFORM_ORG,
133
+ RESERVED_AGENT_PROJECT_IDS,
134
+ LOCAL_AGENT_IDENTITY,
135
+ INITIATOR_HEADER,
136
+ EVE_TURN_HEADER,
137
+ EVE_SESSION_HEADER,
138
+ BUILDER_AGENT_IDENTITY,
139
+ AGENT_TOKEN_HEADER,
140
+ AGENT_TOKEN_ENV
141
+ };
@@ -0,0 +1,287 @@
1
+ // ../../var/folders/vh/qdfcqc514qj47bbvzslcwjsc0000gn/T/agent-sdk-publish-eDWqsZ/src/state-files.ts
2
+ var STATE_FILES_HANDLE_PATH = "/state/handles";
3
+ var ZO_AGENT_TOKEN_HEADER = "x-zo-agent-token";
4
+ var ZO_EVE_SESSION_HEADER = "x-zo-eve-session";
5
+
6
+ class StateFilesHandleError extends Error {
7
+ status;
8
+ code;
9
+ constructor(message, options) {
10
+ super(message);
11
+ this.name = "StateFilesHandleError";
12
+ this.status = options.status;
13
+ this.code = options.code ?? null;
14
+ }
15
+ }
16
+ async function requestStateFilesHandle(options) {
17
+ const response = await options.fetch(buildStateFilesHandleUrl(options.apiBaseUrl), {
18
+ method: "POST",
19
+ headers: buildStateFilesHandleHeaders(options),
20
+ body: JSON.stringify(buildStateFilesHandleRequest(options))
21
+ });
22
+ const json = await response.json().catch(() => null);
23
+ if (!response.ok) {
24
+ const error = parseStateFilesBrokerError(json);
25
+ throw new StateFilesHandleError(error.message, {
26
+ status: response.status,
27
+ code: error.code
28
+ });
29
+ }
30
+ const handle = parseStateFilesHandle(json);
31
+ if (handle === null) {
32
+ throw new StateFilesHandleError("state files broker returned a malformed handle", {
33
+ status: response.status,
34
+ code: "malformed_handle"
35
+ });
36
+ }
37
+ return handle;
38
+ }
39
+ function parseStateFilesHandle(value) {
40
+ if (!isRecord(value)) {
41
+ return null;
42
+ }
43
+ const credentials = parseStateFilesCredentials(value.credentials);
44
+ if (credentials === null) {
45
+ return null;
46
+ }
47
+ const access = parseStateFilesAccess(value.access);
48
+ if (access === null) {
49
+ return null;
50
+ }
51
+ if (value.interface !== "files" || value.engine !== "zo-blob-r2") {
52
+ return null;
53
+ }
54
+ const handleId = readString(value, "handleId");
55
+ const declarationName = readString(value, "declarationName");
56
+ const storeId = readString(value, "storeId");
57
+ const stateInstanceId = readString(value, "stateInstanceId");
58
+ const partition = parseStateFilesPartition(value.partition);
59
+ const bucketName = readString(value, "bucketName");
60
+ const endpoint = readString(value, "endpoint");
61
+ if (handleId === null || declarationName === null || storeId === null || stateInstanceId === null || partition === null || bucketName === null || endpoint === null) {
62
+ return null;
63
+ }
64
+ return Object.freeze({
65
+ handleId,
66
+ declarationName,
67
+ interface: "files",
68
+ access,
69
+ engine: "zo-blob-r2",
70
+ storeId,
71
+ stateInstanceId,
72
+ partition,
73
+ bucketName,
74
+ endpoint,
75
+ credentials
76
+ });
77
+ }
78
+ function createStateFilesClient(options) {
79
+ return createStateFilesClientFromHandleSource({
80
+ getHandle: async () => options.handle,
81
+ s3: options.s3
82
+ });
83
+ }
84
+ function createRefreshingStateFilesClient(options) {
85
+ let cached = null;
86
+ const now = options.now ?? (() => new Date);
87
+ const refreshWindowMs = options.refreshWindowMs ?? 60000;
88
+ return createStateFilesClientFromHandleSource({
89
+ getHandle: async () => {
90
+ if (cached === null || shouldRefreshStateFilesHandle(cached, now(), refreshWindowMs)) {
91
+ cached = await options.loadHandle();
92
+ }
93
+ return cached;
94
+ },
95
+ s3: options.s3
96
+ });
97
+ }
98
+ function shouldRefreshStateFilesHandle(handle, now, refreshWindowMs = 60000) {
99
+ const expiresAtMs = Date.parse(handle.credentials.expiresAt);
100
+ if (!Number.isFinite(expiresAtMs)) {
101
+ return true;
102
+ }
103
+ return expiresAtMs - now.getTime() <= refreshWindowMs;
104
+ }
105
+ function createStateFilesClientFromHandleSource(options) {
106
+ return {
107
+ async list(prefix) {
108
+ const handle = await options.getHandle();
109
+ const normalizedPrefix = normalizeStateFilePrefix(prefix);
110
+ return options.s3.listObjects({
111
+ ...buildS3Context(handle),
112
+ ...normalizedPrefix === undefined ? {} : { prefix: normalizedPrefix }
113
+ });
114
+ },
115
+ async read(path) {
116
+ const handle = await options.getHandle();
117
+ return options.s3.readObject({
118
+ ...buildS3Context(handle),
119
+ key: normalizeStateFilePath(path)
120
+ });
121
+ },
122
+ async write(path, body, writeOptions) {
123
+ const handle = await options.getHandle();
124
+ assertStateFilesWriteAccess(handle);
125
+ return options.s3.writeObject({
126
+ ...buildS3Context(handle),
127
+ key: normalizeStateFilePath(path),
128
+ body,
129
+ ...writeOptions?.contentType === undefined ? {} : { contentType: writeOptions.contentType }
130
+ });
131
+ },
132
+ async delete(path) {
133
+ const handle = await options.getHandle();
134
+ assertStateFilesWriteAccess(handle);
135
+ return options.s3.deleteObject({
136
+ ...buildS3Context(handle),
137
+ key: normalizeStateFilePath(path)
138
+ });
139
+ }
140
+ };
141
+ }
142
+ function normalizeStateFilePath(path) {
143
+ if (path.length === 0) {
144
+ throw new Error("state file path must not be empty");
145
+ }
146
+ if (path.startsWith("/")) {
147
+ throw new Error(`state file path "${path}" must be relative`);
148
+ }
149
+ const segments = path.split("/");
150
+ if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..")) {
151
+ throw new Error(`state file path "${path}" must not contain empty, . or .. segments`);
152
+ }
153
+ return path;
154
+ }
155
+ function normalizeStateFilePrefix(prefix) {
156
+ if (prefix === undefined || prefix.length === 0) {
157
+ return;
158
+ }
159
+ if (prefix.endsWith("/")) {
160
+ return normalizeStateFilePath(prefix.slice(0, -1)) + "/";
161
+ }
162
+ return normalizeStateFilePath(prefix);
163
+ }
164
+ function buildStateFilesHandleRequest(options) {
165
+ const request = {
166
+ declarationName: options.declarationName,
167
+ interface: "files",
168
+ access: options.access
169
+ };
170
+ return request;
171
+ }
172
+ function buildStateFilesHandleUrl(apiBaseUrl) {
173
+ const url = new URL(String(apiBaseUrl));
174
+ url.pathname = joinUrlPath(url.pathname, STATE_FILES_HANDLE_PATH);
175
+ url.search = "";
176
+ url.hash = "";
177
+ return url.toString();
178
+ }
179
+ function joinUrlPath(basePath, childPath) {
180
+ const base = basePath.replace(/\/+$/, "");
181
+ const child = childPath.replace(/^\/+/, "");
182
+ return `${base}/${child}`;
183
+ }
184
+ function buildStateFilesHandleHeaders(options) {
185
+ const headers = createHeaders(options.headers);
186
+ headers.set("content-type", "application/json");
187
+ if (options.agentToken !== undefined) {
188
+ headers.set(ZO_AGENT_TOKEN_HEADER, options.agentToken);
189
+ }
190
+ if (options.eveSessionKey !== undefined) {
191
+ headers.set(ZO_EVE_SESSION_HEADER, options.eveSessionKey);
192
+ }
193
+ return headers;
194
+ }
195
+ function createHeaders(init) {
196
+ const headers = new Headers;
197
+ if (init === undefined) {
198
+ return headers;
199
+ }
200
+ if (init instanceof Headers) {
201
+ init.forEach((value, key) => headers.set(key, value));
202
+ return headers;
203
+ }
204
+ if (Array.isArray(init)) {
205
+ for (const [key, value] of init) {
206
+ headers.set(key, value);
207
+ }
208
+ return headers;
209
+ }
210
+ for (const [key, value] of Object.entries(init)) {
211
+ headers.set(key, value);
212
+ }
213
+ return headers;
214
+ }
215
+ function parseStateFilesBrokerError(value) {
216
+ if (!isRecord(value)) {
217
+ return { message: "state files broker request failed", code: null };
218
+ }
219
+ const routeError = readString(value, "error");
220
+ if (routeError !== null) {
221
+ return {
222
+ message: readString(value, "message") ?? "state files broker request failed",
223
+ code: routeError
224
+ };
225
+ }
226
+ const error = isRecord(value.error) ? value.error : value;
227
+ const message = readString(error, "message") ?? "state files broker request failed";
228
+ const code = readString(error, "code") ?? readString(error, "error");
229
+ return { message, code };
230
+ }
231
+ function parseStateFilesCredentials(value) {
232
+ if (!isRecord(value)) {
233
+ return null;
234
+ }
235
+ const accessKeyId = readString(value, "accessKeyId");
236
+ const secretAccessKey = readString(value, "secretAccessKey");
237
+ const sessionToken = readString(value, "sessionToken");
238
+ const expiresAt = readString(value, "expiresAt");
239
+ if (accessKeyId === null || secretAccessKey === null || sessionToken === null || expiresAt === null || !Number.isFinite(Date.parse(expiresAt))) {
240
+ return null;
241
+ }
242
+ return Object.freeze({ accessKeyId, secretAccessKey, sessionToken, expiresAt });
243
+ }
244
+ function parseStateFilesPartition(value) {
245
+ if (value === "none" || value === "team" || value === "user" || value === "session") {
246
+ return value;
247
+ }
248
+ return null;
249
+ }
250
+ function parseStateFilesAccess(value) {
251
+ if (value === "r" || value === "rw") {
252
+ return value;
253
+ }
254
+ return null;
255
+ }
256
+ function buildS3Context(handle) {
257
+ return {
258
+ endpoint: handle.endpoint,
259
+ bucketName: handle.bucketName,
260
+ credentials: handle.credentials
261
+ };
262
+ }
263
+ function assertStateFilesWriteAccess(handle) {
264
+ if (handle.access !== "rw") {
265
+ throw new Error(`state files handle "${handle.handleId}" is read-only`);
266
+ }
267
+ }
268
+ function isRecord(value) {
269
+ return typeof value === "object" && value !== null && !Array.isArray(value);
270
+ }
271
+ function readString(record, key) {
272
+ const value = record[key];
273
+ return typeof value === "string" && value.length > 0 ? value : null;
274
+ }
275
+ export {
276
+ shouldRefreshStateFilesHandle,
277
+ requestStateFilesHandle,
278
+ parseStateFilesHandle,
279
+ normalizeStateFilePrefix,
280
+ normalizeStateFilePath,
281
+ createStateFilesClient,
282
+ createRefreshingStateFilesClient,
283
+ ZO_EVE_SESSION_HEADER,
284
+ ZO_AGENT_TOKEN_HEADER,
285
+ StateFilesHandleError,
286
+ STATE_FILES_HANDLE_PATH
287
+ };