@hypercli.com/sdk 0.9.1

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 (193) hide show
  1. package/README.md +629 -0
  2. package/dist/acp-driver.d.ts +72 -0
  3. package/dist/acp-driver.d.ts.map +1 -0
  4. package/dist/acp-driver.js +147 -0
  5. package/dist/acp-driver.js.map +1 -0
  6. package/dist/acp-pool.d.ts +70 -0
  7. package/dist/acp-pool.d.ts.map +1 -0
  8. package/dist/acp-pool.js +181 -0
  9. package/dist/acp-pool.js.map +1 -0
  10. package/dist/acp.d.ts +179 -0
  11. package/dist/acp.d.ts.map +1 -0
  12. package/dist/acp.js +487 -0
  13. package/dist/acp.js.map +1 -0
  14. package/dist/agent-persona.d.ts +68 -0
  15. package/dist/agent-persona.d.ts.map +1 -0
  16. package/dist/agent-persona.js +78 -0
  17. package/dist/agent-persona.js.map +1 -0
  18. package/dist/agent-slots.d.ts +18 -0
  19. package/dist/agent-slots.d.ts.map +1 -0
  20. package/dist/agent-slots.js +24 -0
  21. package/dist/agent-slots.js.map +1 -0
  22. package/dist/agent-urls.d.ts +47 -0
  23. package/dist/agent-urls.d.ts.map +1 -0
  24. package/dist/agent-urls.js +178 -0
  25. package/dist/agent-urls.js.map +1 -0
  26. package/dist/agent.d.ts +482 -0
  27. package/dist/agent.d.ts.map +1 -0
  28. package/dist/agent.js +926 -0
  29. package/dist/agent.js.map +1 -0
  30. package/dist/agents.d.ts +1452 -0
  31. package/dist/agents.d.ts.map +1 -0
  32. package/dist/agents.js +4022 -0
  33. package/dist/agents.js.map +1 -0
  34. package/dist/billing.d.ts +72 -0
  35. package/dist/billing.d.ts.map +1 -0
  36. package/dist/billing.js +93 -0
  37. package/dist/billing.js.map +1 -0
  38. package/dist/browser-jobs.d.ts +24 -0
  39. package/dist/browser-jobs.d.ts.map +1 -0
  40. package/dist/browser-jobs.js +209 -0
  41. package/dist/browser-jobs.js.map +1 -0
  42. package/dist/browser.d.ts +41 -0
  43. package/dist/browser.d.ts.map +1 -0
  44. package/dist/browser.js +57 -0
  45. package/dist/browser.js.map +1 -0
  46. package/dist/buzz-activity.d.ts +169 -0
  47. package/dist/buzz-activity.d.ts.map +1 -0
  48. package/dist/buzz-activity.js +844 -0
  49. package/dist/buzz-activity.js.map +1 -0
  50. package/dist/channels.d.ts +99 -0
  51. package/dist/channels.d.ts.map +1 -0
  52. package/dist/channels.js +27 -0
  53. package/dist/channels.js.map +1 -0
  54. package/dist/chat-integrations.d.ts +44 -0
  55. package/dist/chat-integrations.d.ts.map +1 -0
  56. package/dist/chat-integrations.js +138 -0
  57. package/dist/chat-integrations.js.map +1 -0
  58. package/dist/client.d.ts +88 -0
  59. package/dist/client.d.ts.map +1 -0
  60. package/dist/client.js +144 -0
  61. package/dist/client.js.map +1 -0
  62. package/dist/config.d.ts +44 -0
  63. package/dist/config.d.ts.map +1 -0
  64. package/dist/config.js +192 -0
  65. package/dist/config.js.map +1 -0
  66. package/dist/connectors.d.ts +80 -0
  67. package/dist/connectors.d.ts.map +1 -0
  68. package/dist/connectors.js +2 -0
  69. package/dist/connectors.js.map +1 -0
  70. package/dist/errors.d.ts +16 -0
  71. package/dist/errors.d.ts.map +1 -0
  72. package/dist/errors.js +26 -0
  73. package/dist/errors.js.map +1 -0
  74. package/dist/files.d.ts +60 -0
  75. package/dist/files.d.ts.map +1 -0
  76. package/dist/files.js +128 -0
  77. package/dist/files.js.map +1 -0
  78. package/dist/http.d.ts +56 -0
  79. package/dist/http.d.ts.map +1 -0
  80. package/dist/http.js +362 -0
  81. package/dist/http.js.map +1 -0
  82. package/dist/index.d.ts +49 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +64 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/instances.d.ts +94 -0
  87. package/dist/instances.d.ts.map +1 -0
  88. package/dist/instances.js +197 -0
  89. package/dist/instances.js.map +1 -0
  90. package/dist/integrations.d.ts +111 -0
  91. package/dist/integrations.d.ts.map +1 -0
  92. package/dist/integrations.js +206 -0
  93. package/dist/integrations.js.map +1 -0
  94. package/dist/job/base.d.ts +86 -0
  95. package/dist/job/base.d.ts.map +1 -0
  96. package/dist/job/base.js +206 -0
  97. package/dist/job/base.js.map +1 -0
  98. package/dist/job/comfyui.d.ts +259 -0
  99. package/dist/job/comfyui.d.ts.map +1 -0
  100. package/dist/job/comfyui.js +1563 -0
  101. package/dist/job/comfyui.js.map +1 -0
  102. package/dist/job/gradio.d.ts +48 -0
  103. package/dist/job/gradio.d.ts.map +1 -0
  104. package/dist/job/gradio.js +90 -0
  105. package/dist/job/gradio.js.map +1 -0
  106. package/dist/job-codecs.d.ts +66 -0
  107. package/dist/job-codecs.d.ts.map +1 -0
  108. package/dist/job-codecs.js +132 -0
  109. package/dist/job-codecs.js.map +1 -0
  110. package/dist/jobs.d.ts +148 -0
  111. package/dist/jobs.d.ts.map +1 -0
  112. package/dist/jobs.js +480 -0
  113. package/dist/jobs.js.map +1 -0
  114. package/dist/keys.d.ts +99 -0
  115. package/dist/keys.d.ts.map +1 -0
  116. package/dist/keys.js +145 -0
  117. package/dist/keys.js.map +1 -0
  118. package/dist/logs.d.ts +54 -0
  119. package/dist/logs.d.ts.map +1 -0
  120. package/dist/logs.js +256 -0
  121. package/dist/logs.js.map +1 -0
  122. package/dist/managed-context.d.ts +20 -0
  123. package/dist/managed-context.d.ts.map +1 -0
  124. package/dist/managed-context.js +95 -0
  125. package/dist/managed-context.js.map +1 -0
  126. package/dist/mcp.d.ts +7 -0
  127. package/dist/mcp.d.ts.map +1 -0
  128. package/dist/mcp.js +8 -0
  129. package/dist/mcp.js.map +1 -0
  130. package/dist/memory.d.ts +87 -0
  131. package/dist/memory.d.ts.map +1 -0
  132. package/dist/memory.js +79 -0
  133. package/dist/memory.js.map +1 -0
  134. package/dist/models.d.ts +15 -0
  135. package/dist/models.d.ts.map +1 -0
  136. package/dist/models.js +19 -0
  137. package/dist/models.js.map +1 -0
  138. package/dist/openclaw-control-ui-origin.d.ts +23 -0
  139. package/dist/openclaw-control-ui-origin.d.ts.map +1 -0
  140. package/dist/openclaw-control-ui-origin.js +53 -0
  141. package/dist/openclaw-control-ui-origin.js.map +1 -0
  142. package/dist/renders.d.ts +187 -0
  143. package/dist/renders.d.ts.map +1 -0
  144. package/dist/renders.js +360 -0
  145. package/dist/renders.js.map +1 -0
  146. package/dist/routines.d.ts +59 -0
  147. package/dist/routines.d.ts.map +1 -0
  148. package/dist/routines.js +135 -0
  149. package/dist/routines.js.map +1 -0
  150. package/dist/runners.d.ts +61 -0
  151. package/dist/runners.d.ts.map +1 -0
  152. package/dist/runners.js +127 -0
  153. package/dist/runners.js.map +1 -0
  154. package/dist/sessions.d.ts +280 -0
  155. package/dist/sessions.d.ts.map +1 -0
  156. package/dist/sessions.js +292 -0
  157. package/dist/sessions.js.map +1 -0
  158. package/dist/skills.d.ts +170 -0
  159. package/dist/skills.d.ts.map +1 -0
  160. package/dist/skills.js +2 -0
  161. package/dist/skills.js.map +1 -0
  162. package/dist/user.d.ts +78 -0
  163. package/dist/user.d.ts.map +1 -0
  164. package/dist/user.js +143 -0
  165. package/dist/user.js.map +1 -0
  166. package/dist/version.d.ts +2 -0
  167. package/dist/version.d.ts.map +1 -0
  168. package/dist/version.js +3 -0
  169. package/dist/version.js.map +1 -0
  170. package/dist/voice-session.d.ts +66 -0
  171. package/dist/voice-session.d.ts.map +1 -0
  172. package/dist/voice-session.js +366 -0
  173. package/dist/voice-session.js.map +1 -0
  174. package/dist/voice-transcription-session.d.ts +64 -0
  175. package/dist/voice-transcription-session.d.ts.map +1 -0
  176. package/dist/voice-transcription-session.js +228 -0
  177. package/dist/voice-transcription-session.js.map +1 -0
  178. package/dist/voice.d.ts +62 -0
  179. package/dist/voice.d.ts.map +1 -0
  180. package/dist/voice.js +134 -0
  181. package/dist/voice.js.map +1 -0
  182. package/dist/workspace-tomd.d.ts +61 -0
  183. package/dist/workspace-tomd.d.ts.map +1 -0
  184. package/dist/workspace-tomd.js +177 -0
  185. package/dist/workspace-tomd.js.map +1 -0
  186. package/dist/workspaces.d.ts +255 -0
  187. package/dist/workspaces.d.ts.map +1 -0
  188. package/dist/workspaces.js +653 -0
  189. package/dist/workspaces.js.map +1 -0
  190. package/package.json +115 -0
  191. package/runtime/config-require.browser.js +4 -0
  192. package/runtime/config-require.d.ts +2 -0
  193. package/runtime/config-require.node.js +8 -0
@@ -0,0 +1,653 @@
1
+ /**
2
+ * Shared knowledge API - shared Markdown-backed file collections.
3
+ */
4
+ import { requestWithRetry } from './http.js';
5
+ import { APIError } from './errors.js';
6
+ import { getAgentsApiBaseUrl } from './config.js';
7
+ import { parseWorkspaceTomd } from './workspace-tomd.js';
8
+ export { parseWorkspaceTomd, stripWorkspaceTomdGeneratedSections } from './workspace-tomd.js';
9
+ // Node-only builtins are loaded through an opaque specifier so browser bundles
10
+ // (console, desktop webview) never try to resolve them; the sync helpers below
11
+ // throw a clear error when called outside Node.
12
+ async function loadNodeBuiltin(moduleName) {
13
+ try {
14
+ return (await import(/* webpackIgnore: true */ moduleName));
15
+ }
16
+ catch {
17
+ throw new Error(`shared knowledge sync requires a Node.js runtime (${moduleName} is unavailable)`);
18
+ }
19
+ }
20
+ export function deriveWorkspacesApiBase(agentsApiBase) {
21
+ const raw = (agentsApiBase || getAgentsApiBaseUrl()).replace(/\/$/, '');
22
+ const url = new URL(raw.includes('://') ? raw : `https://${raw}`);
23
+ let path = url.pathname.replace(/\/$/, '');
24
+ if (path.endsWith('/workspaces')) {
25
+ return `${url.protocol}//${url.host}${path}`;
26
+ }
27
+ if (path.endsWith('/agents')) {
28
+ path = path.slice(0, -'/agents'.length);
29
+ }
30
+ return `${url.protocol}//${url.host}${path}/workspaces`;
31
+ }
32
+ function workspaceFromDict(data) {
33
+ return {
34
+ id: String(data?.id || ''),
35
+ name: data?.name || '',
36
+ slug: data?.slug || '',
37
+ description: data?.description ?? null,
38
+ displayName: data?.display_name ?? data?.displayName ?? null,
39
+ displaySlug: data?.display_slug ?? data?.displaySlug ?? null,
40
+ role: data?.role ?? data?.current_role ?? data?.currentRole ?? null,
41
+ createdAt: data?.created_at ?? data?.createdAt ?? null,
42
+ updatedAt: data?.updated_at ?? data?.updatedAt ?? null,
43
+ };
44
+ }
45
+ function grantFromDict(data) {
46
+ return {
47
+ id: String(data?.id || ''),
48
+ workspaceId: String(data?.workspace_id || data?.workspaceId || ''),
49
+ subjectType: data?.subject_type || data?.subjectType || '',
50
+ subjectId: data?.subject_id || data?.subjectId || '',
51
+ role: data?.role || '',
52
+ displayName: data?.display_name ?? data?.displayName ?? null,
53
+ displaySlug: data?.display_slug ?? data?.displaySlug ?? null,
54
+ isOwner: Boolean(data?.is_owner ?? data?.isOwner ?? false),
55
+ expiresAt: data?.expires_at ?? data?.expiresAt ?? null,
56
+ revokedAt: data?.revoked_at ?? data?.revokedAt ?? null,
57
+ };
58
+ }
59
+ const WORKSPACE_ROLE_STRENGTH = {
60
+ viewer: 1,
61
+ contributor: 2,
62
+ admin: 3,
63
+ };
64
+ function grantExpirationTimestamp(grant) {
65
+ if (grant.expiresAt === null)
66
+ return null;
67
+ if (typeof grant.expiresAt !== 'string') {
68
+ throw new Error(`Invalid expiration timestamp for workspace grant ${grant.id || '<unknown>'}: ${String(grant.expiresAt)}`);
69
+ }
70
+ const timestamp = Date.parse(grant.expiresAt);
71
+ if (!Number.isFinite(timestamp)) {
72
+ throw new Error(`Invalid expiration timestamp for workspace grant ${grant.id || '<unknown>'}: ${grant.expiresAt}`);
73
+ }
74
+ return timestamp;
75
+ }
76
+ function strongestWorkspaceRole(grants) {
77
+ let strongest = '';
78
+ let strongestValue = -1;
79
+ for (const grant of grants) {
80
+ const value = WORKSPACE_ROLE_STRENGTH[grant.role] ?? 0;
81
+ if (value > strongestValue || (value === strongestValue && grant.role < strongest)) {
82
+ strongest = grant.role;
83
+ strongestValue = value;
84
+ }
85
+ }
86
+ return strongest;
87
+ }
88
+ function agreedNonEmptyValue(values) {
89
+ const nonEmpty = new Set(values.filter((value) => typeof value === 'string' && value.length > 0));
90
+ return nonEmpty.size === 1 ? nonEmpty.values().next().value ?? null : null;
91
+ }
92
+ function workspaceAccessEntries(grants, capturedAt) {
93
+ const grouped = new Map();
94
+ for (const grant of grants) {
95
+ const expiresAt = grantExpirationTimestamp(grant);
96
+ if (grant.revokedAt !== null || (expiresAt !== null && expiresAt <= capturedAt))
97
+ continue;
98
+ const key = JSON.stringify([grant.workspaceId, grant.subjectType, grant.subjectId]);
99
+ let group = grouped.get(key);
100
+ if (!group) {
101
+ group = {
102
+ workspaceId: grant.workspaceId,
103
+ subjectType: grant.subjectType,
104
+ subjectId: grant.subjectId,
105
+ grants: [],
106
+ };
107
+ grouped.set(key, group);
108
+ }
109
+ group.grants.push(grant);
110
+ }
111
+ return Array.from(grouped.values(), (group) => ({
112
+ workspaceId: group.workspaceId,
113
+ subjectType: group.subjectType,
114
+ subjectId: group.subjectId,
115
+ role: strongestWorkspaceRole(group.grants),
116
+ displayName: agreedNonEmptyValue(group.grants.map((grant) => grant.displayName)),
117
+ displaySlug: agreedNonEmptyValue(group.grants.map((grant) => grant.displaySlug)),
118
+ grants: group.grants,
119
+ })).sort((left, right) => {
120
+ if (left.subjectType !== right.subjectType)
121
+ return left.subjectType < right.subjectType ? -1 : 1;
122
+ if (left.subjectId !== right.subjectId)
123
+ return left.subjectId < right.subjectId ? -1 : 1;
124
+ if (left.workspaceId !== right.workspaceId)
125
+ return left.workspaceId < right.workspaceId ? -1 : 1;
126
+ return 0;
127
+ });
128
+ }
129
+ function latestGrantExpiration(grants) {
130
+ if (grants.some((grant) => grant.expiresAt === null))
131
+ return null;
132
+ let latestValue = null;
133
+ let latestTimestamp = -Infinity;
134
+ for (const grant of grants) {
135
+ const value = grant.expiresAt;
136
+ const timestamp = grantExpirationTimestamp(grant);
137
+ if (timestamp > latestTimestamp || (timestamp === latestTimestamp && (latestValue === null || value > latestValue))) {
138
+ latestValue = value;
139
+ latestTimestamp = timestamp;
140
+ }
141
+ }
142
+ return latestValue;
143
+ }
144
+ function fileFromDict(data) {
145
+ return {
146
+ id: String(data?.id || ''),
147
+ workspaceId: String(data?.workspace_id || data?.workspaceId || ''),
148
+ path: data?.path || '',
149
+ displayName: data?.display_name || data?.displayName || '',
150
+ currentVersionId: data?.current_version_id || data?.currentVersionId || null,
151
+ fileState: data?.file_state || data?.fileState || '',
152
+ uploadStatus: data?.upload_status || data?.uploadStatus || null,
153
+ processingState: data?.processing_state || data?.processingState || null,
154
+ keywords: Array.isArray(data?.keywords) ? data.keywords.map(String) : [],
155
+ summary: data?.summary || null,
156
+ };
157
+ }
158
+ function fileSearchResultFromDict(data) {
159
+ return {
160
+ ...fileFromDict(data),
161
+ matchReasons: data?.match_reasons || data?.matchReasons || [],
162
+ keywordScore: Number(data?.keyword_score ?? data?.keywordScore ?? 0),
163
+ vectorScore: data?.vector_score !== undefined && data?.vector_score !== null
164
+ ? Number(data.vector_score)
165
+ : data?.vectorScore !== undefined && data?.vectorScore !== null
166
+ ? Number(data.vectorScore)
167
+ : null,
168
+ score: Number(data?.score ?? 0),
169
+ };
170
+ }
171
+ function searchPageFromDict(payload) {
172
+ if (Array.isArray(payload)) {
173
+ return { results: payload.map(fileSearchResultFromDict), nextCursor: null, hasMore: false };
174
+ }
175
+ const data = (payload && typeof payload === 'object' ? payload : {});
176
+ const rows = Array.isArray(data.items) ? data.items : Array.isArray(data.results) ? data.results : [];
177
+ const cursor = data.next_cursor ?? data.nextCursor;
178
+ return {
179
+ results: rows.map(fileSearchResultFromDict),
180
+ nextCursor: typeof cursor === 'string' && cursor ? cursor : null,
181
+ hasMore: (data.has_more ?? data.hasMore) === true,
182
+ };
183
+ }
184
+ function manifestFromDict(data) {
185
+ return {
186
+ workspaceId: String(data?.workspace_id || data?.workspaceId || ''),
187
+ workspaceName: data?.workspace_name || data?.workspaceName || '',
188
+ workspaceSlug: data?.workspace_slug || data?.workspaceSlug || '',
189
+ snapshotId: data?.snapshot_id || data?.snapshotId || '',
190
+ basePath: data?.base_path || data?.basePath || '',
191
+ markdownFiles: Array.isArray(data?.markdown_files) ? data.markdown_files : Array.isArray(data?.markdownFiles) ? data.markdownFiles : [],
192
+ };
193
+ }
194
+ function downloadUrlFromDict(data) {
195
+ return {
196
+ fileId: String(data?.file_id || data?.fileId || ''),
197
+ path: data?.path || '',
198
+ version: Number(data?.version || 0),
199
+ url: data?.url ?? null,
200
+ downloadCommand: data?.download_command || data?.downloadCommand || '',
201
+ };
202
+ }
203
+ async function responseErrorDetail(response) {
204
+ const text = await response.text();
205
+ if (!text)
206
+ return response.statusText;
207
+ try {
208
+ const payload = JSON.parse(text);
209
+ const detail = payload?.detail ?? payload?.message ?? payload?.error;
210
+ if (typeof detail === 'string')
211
+ return detail;
212
+ if (detail !== undefined && detail !== null)
213
+ return JSON.stringify(detail);
214
+ return text;
215
+ }
216
+ catch {
217
+ return text;
218
+ }
219
+ }
220
+ async function handleResponse(response) {
221
+ if (response.status >= 400) {
222
+ throw new APIError(response.status, await responseErrorDetail(response));
223
+ }
224
+ if (response.status === 204 || response.status === 205)
225
+ return undefined;
226
+ const text = await response.text();
227
+ if (!text)
228
+ return undefined;
229
+ return JSON.parse(text);
230
+ }
231
+ async function handleBytesResponse(response) {
232
+ if (response.status >= 400) {
233
+ throw new APIError(response.status, await responseErrorDetail(response));
234
+ }
235
+ return new Uint8Array(await response.arrayBuffer());
236
+ }
237
+ function encodeRef(value) {
238
+ return encodeURIComponent(value);
239
+ }
240
+ function encodeFileRef(value) {
241
+ return normalizePosixPath(value)
242
+ .replace(/^\/+/, '')
243
+ .replace(/\/+$/, '')
244
+ .split('/')
245
+ .map(encodeURIComponent)
246
+ .join('/');
247
+ }
248
+ function workspaceMatchesEnsureOptions(workspace, options) {
249
+ if (options.match?.(workspace))
250
+ return true;
251
+ if (options.slug && workspace.slug === options.slug)
252
+ return true;
253
+ return !options.slug && workspace.name === options.name;
254
+ }
255
+ export class WorkspacesAPI {
256
+ apiBase;
257
+ apiKey;
258
+ timeout;
259
+ uploadTimeout;
260
+ constructor(apiKey, options = {}) {
261
+ if (!apiKey) {
262
+ throw new Error('API key required for shared knowledge');
263
+ }
264
+ this.apiKey = apiKey;
265
+ this.apiBase = (options.apiBase || deriveWorkspacesApiBase(options.agentsApiBase)).replace(/\/$/, '');
266
+ this.timeout = options.timeout ?? 30000;
267
+ this.uploadTimeout = options.uploadTimeout ?? Math.max(this.timeout, 120000);
268
+ }
269
+ headers(_subject = {}) {
270
+ const headers = {
271
+ Authorization: `Bearer ${this.apiKey}`,
272
+ 'Content-Type': 'application/json',
273
+ };
274
+ return headers;
275
+ }
276
+ authHeaders(_subject = {}) {
277
+ const headers = {
278
+ Authorization: `Bearer ${this.apiKey}`,
279
+ };
280
+ return headers;
281
+ }
282
+ async request(method, path, subject = {}, body) {
283
+ const response = await requestWithRetry({
284
+ method,
285
+ url: `${this.apiBase}${path}`,
286
+ headers: this.headers(subject),
287
+ body,
288
+ retries: method === 'GET' ? 3 : 1,
289
+ timeout: this.timeout,
290
+ });
291
+ return handleResponse(response);
292
+ }
293
+ async list(subject = {}) {
294
+ const data = await this.request('GET', '', subject);
295
+ return (data || []).map(workspaceFromDict);
296
+ }
297
+ async get(workspaceRef, subject = {}) {
298
+ const data = await this.request('GET', `/${encodeRef(workspaceRef)}`, subject);
299
+ return workspaceFromDict(data);
300
+ }
301
+ async accessSnapshot(workspaceRef, subject = {}) {
302
+ const workspace = await this.get(workspaceRef, subject);
303
+ const capturedTime = new Date();
304
+ const capturedAt = capturedTime.toISOString();
305
+ const currentRole = workspace.role;
306
+ if (currentRole !== 'admin') {
307
+ return {
308
+ workspace,
309
+ currentRole,
310
+ visibility: 'current-access-only',
311
+ capturedAt,
312
+ entries: null,
313
+ grants: null,
314
+ };
315
+ }
316
+ const grants = await this.listGrants(workspaceRef, subject);
317
+ return {
318
+ workspace,
319
+ currentRole,
320
+ visibility: 'all-direct-access',
321
+ capturedAt,
322
+ entries: workspaceAccessEntries(grants, capturedTime.getTime()),
323
+ grants,
324
+ };
325
+ }
326
+ async listAgentAssociations(workspaceRef, subject = {}) {
327
+ const snapshot = await this.accessSnapshot(workspaceRef, subject);
328
+ if (snapshot.visibility !== 'all-direct-access' || snapshot.entries === null) {
329
+ throw new Error('Workspace agent associations are available only to Workspace admins.');
330
+ }
331
+ return snapshot.entries
332
+ .filter((entry) => entry.subjectType === 'agent')
333
+ .map((entry) => ({
334
+ workspaceId: entry.workspaceId,
335
+ agentId: entry.subjectId,
336
+ role: entry.role,
337
+ expiresAt: latestGrantExpiration(entry.grants),
338
+ }));
339
+ }
340
+ async search(query, subject = {}, options = {}) {
341
+ const params = new URLSearchParams({ q: query, vector: String(options.vector ?? true) });
342
+ const data = await this.request('GET', `/search?${params.toString()}`, subject);
343
+ return (data || []).map(workspaceFromDict);
344
+ }
345
+ async create(body, subject = {}) {
346
+ const data = await this.request('POST', '', subject, body);
347
+ return workspaceFromDict(data);
348
+ }
349
+ async ensureWorkspace(options, subject = {}) {
350
+ const listed = await this.list(subject);
351
+ const existing = listed.find((workspace) => workspaceMatchesEnsureOptions(workspace, options));
352
+ if (existing)
353
+ return { workspace: existing, created: false };
354
+ try {
355
+ return {
356
+ workspace: await this.create({
357
+ name: options.name,
358
+ ...(options.slug !== undefined ? { slug: options.slug } : {}),
359
+ ...(options.description !== undefined ? { description: options.description } : {}),
360
+ }, subject),
361
+ created: true,
362
+ };
363
+ }
364
+ catch (error) {
365
+ if (!(error instanceof APIError) || error.statusCode !== 409)
366
+ throw error;
367
+ const recovered = await this.list(subject);
368
+ const recoveredWorkspace = recovered.find((workspace) => workspaceMatchesEnsureOptions(workspace, options));
369
+ if (recoveredWorkspace)
370
+ return { workspace: recoveredWorkspace, created: false };
371
+ throw error;
372
+ }
373
+ }
374
+ async update(workspaceRef, body, subject = {}) {
375
+ const data = await this.request('PATCH', `/${encodeRef(workspaceRef)}`, subject, body);
376
+ return workspaceFromDict(data);
377
+ }
378
+ async delete(workspaceRef, subject = {}) {
379
+ await this.request('DELETE', `/${encodeRef(workspaceRef)}`, subject);
380
+ }
381
+ async grant(workspaceRef, body, subject = {}) {
382
+ const data = await this.request('POST', `/${encodeRef(workspaceRef)}/grants`, subject, {
383
+ subject_type: body.subjectType,
384
+ subject_id: body.subjectId,
385
+ role: body.role ?? 'viewer',
386
+ ...(body.displayName !== undefined ? { display_name: body.displayName } : {}),
387
+ ...(body.displaySlug !== undefined ? { display_slug: body.displaySlug } : {}),
388
+ ...(body.expiresAt !== undefined ? { expires_at: body.expiresAt } : {}),
389
+ });
390
+ return grantFromDict(data);
391
+ }
392
+ async listGrants(workspaceRef, subject = {}) {
393
+ const data = await this.request('GET', `/${encodeRef(workspaceRef)}/grants`, subject);
394
+ if (!Array.isArray(data)) {
395
+ throw new Error('Workspace grants response must be an array.');
396
+ }
397
+ return data.map(grantFromDict);
398
+ }
399
+ async updateGrant(workspaceRef, grantId, body, subject = {}) {
400
+ const data = await this.request('PATCH', `/${encodeRef(workspaceRef)}/grants/${encodeRef(grantId)}`, subject, {
401
+ ...(body.role !== undefined ? { role: body.role } : {}),
402
+ ...(body.expiresAt !== undefined ? { expires_at: body.expiresAt } : {}),
403
+ });
404
+ return grantFromDict(data);
405
+ }
406
+ /**
407
+ * Accept a workspace invite addressed to the signed-in user's email
408
+ * (`POST /invites/accept/{grant_id}`). The grant id arrives out-of-band via
409
+ * the invite email link; there is no pending-invites listing endpoint.
410
+ * Errors surface as APIError: 404 unknown grant, 403 wrong email or non-user
411
+ * caller, 409 revoked/expired/already-accepted.
412
+ */
413
+ async acceptInvite(grantId, subject = {}) {
414
+ const data = await this.request('POST', `/invites/accept/${encodeRef(grantId)}`, subject);
415
+ return grantFromDict(data);
416
+ }
417
+ async revokeGrant(workspaceRef, grantId, subject = {}) {
418
+ await this.request('DELETE', `/${encodeRef(workspaceRef)}/grants/${encodeRef(grantId)}`, subject);
419
+ }
420
+ async registerFile(workspaceRef, body, subject = {}) {
421
+ const data = await this.request('POST', `/${encodeRef(workspaceRef)}/files`, subject, {
422
+ path: body.path,
423
+ source_filename: body.sourceFilename,
424
+ source_content_type: body.sourceContentType,
425
+ source_size_bytes: body.sourceSizeBytes,
426
+ source_sha256: body.sourceSha256,
427
+ source_etag: body.sourceEtag,
428
+ keywords: body.keywords,
429
+ });
430
+ return fileFromDict(data);
431
+ }
432
+ async uploadFile(workspaceRef, file, options = {}, subject = {}) {
433
+ const formData = new FormData();
434
+ const filename = options.filename || (typeof File !== 'undefined' && file instanceof File ? file.name : 'upload');
435
+ formData.append('workspace', workspaceRef);
436
+ formData.append('file', file, filename);
437
+ if (options.path)
438
+ formData.append('path', options.path);
439
+ if (options.sourceEtag)
440
+ formData.append('source_etag', options.sourceEtag);
441
+ const controller = new AbortController();
442
+ const timeoutId = setTimeout(() => controller.abort(), this.uploadTimeout);
443
+ try {
444
+ const response = await fetch(`${this.apiBase}/upload`, {
445
+ method: 'POST',
446
+ headers: this.authHeaders(subject),
447
+ body: formData,
448
+ signal: controller.signal,
449
+ });
450
+ return fileFromDict(await handleResponse(response));
451
+ }
452
+ finally {
453
+ clearTimeout(timeoutId);
454
+ }
455
+ }
456
+ async getFile(workspaceRef, fileRef, subject = {}) {
457
+ const data = await this.request('GET', `/${encodeRef(workspaceRef)}/files/${encodeFileRef(fileRef)}`, subject);
458
+ return fileFromDict(data);
459
+ }
460
+ async updateFile(workspaceRef, fileRef, body, subject = {}) {
461
+ const data = await this.request('PATCH', `/${encodeRef(workspaceRef)}/files/${encodeFileRef(fileRef)}`, subject, {
462
+ ...(body.displayName !== undefined ? { display_name: body.displayName } : {}),
463
+ ...(body.keywords !== undefined ? { keywords: body.keywords } : {}),
464
+ ...(body.summary !== undefined ? { summary: body.summary } : {}),
465
+ });
466
+ return fileFromDict(data);
467
+ }
468
+ async regenerateFile(workspaceRef, fileRef, subject = {}) {
469
+ const data = await this.request('POST', `/${encodeRef(workspaceRef)}/files/${encodeFileRef(fileRef)}/regenerate`, subject);
470
+ return fileFromDict(data);
471
+ }
472
+ async waitUntilProcessed(workspaceRef, fileRef, subject = {}, options = {}) {
473
+ const timeoutMs = options.timeoutMs ?? 300000;
474
+ const pollIntervalMs = options.pollIntervalMs ?? 2000;
475
+ const start = Date.now();
476
+ while (Date.now() - start < timeoutMs) {
477
+ const file = await this.getFile(workspaceRef, fileRef, subject);
478
+ if (file.fileState === 'processed' && file.processingState === 'processed') {
479
+ return file;
480
+ }
481
+ if (file.fileState === 'failed' || file.fileState === 'deleted' || file.processingState === 'failed' || file.processingState === 'deleted') {
482
+ throw new Error(`Shared knowledge file ${fileRef} is ${file.fileState} with processing ${file.processingState || 'unknown'}`);
483
+ }
484
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
485
+ }
486
+ throw new Error(`Shared knowledge file ${fileRef} did not process within ${timeoutMs}ms`);
487
+ }
488
+ async listFiles(workspaceRef, subject = {}) {
489
+ const data = await this.request('GET', `/${encodeRef(workspaceRef)}/files`, subject);
490
+ return (data || []).map(fileFromDict);
491
+ }
492
+ /**
493
+ * Search workspace files. `vector` is only sent when explicitly set —
494
+ * omitted defers to the backend default (`true`, keyword-only until the
495
+ * hybrid branch ships). `cursor`/`limit`/`pathPrefix` ride the query string for
496
+ * the paginated backend; today's route ignores them and answers with a bare
497
+ * array, which surfaces as `{ results, nextCursor: null, hasMore: false }`.
498
+ */
499
+ async searchFiles(workspaceRef, query, subject = {}, options = {}) {
500
+ const params = new URLSearchParams({ q: query });
501
+ if (options.vector !== undefined)
502
+ params.set('vector', String(options.vector));
503
+ if (options.cursor)
504
+ params.set('cursor', options.cursor);
505
+ if (options.limit !== undefined)
506
+ params.set('limit', String(options.limit));
507
+ if (options.pathPrefix)
508
+ params.set('path_prefix', options.pathPrefix);
509
+ const payload = await this.request('GET', `/${encodeRef(workspaceRef)}/files/search?${params.toString()}`, subject);
510
+ return searchPageFromDict(payload);
511
+ }
512
+ async manifest(workspaceRef, subject = {}) {
513
+ const data = await this.request('GET', `/${encodeRef(workspaceRef)}/manifest`, subject);
514
+ return manifestFromDict(data);
515
+ }
516
+ async downloadUrl(workspaceRef, fileRef, subject = {}) {
517
+ const data = await this.request('POST', `/download-url`, subject, { workspace: workspaceRef, path: fileRef });
518
+ return downloadUrlFromDict(data);
519
+ }
520
+ async downloadFileBytes(workspaceRef, fileRef, subject = {}, options = {}) {
521
+ const response = await requestWithRetry({
522
+ method: 'POST',
523
+ url: `${this.apiBase}/download`,
524
+ headers: this.headers(subject),
525
+ body: { workspace: workspaceRef, path: fileRef, raw: Boolean(options.raw), index: options.index ?? 1 },
526
+ timeout: this.timeout,
527
+ });
528
+ const path = fileRef;
529
+ return {
530
+ content: await handleBytesResponse(response),
531
+ path,
532
+ name: fileNameFromPath(path),
533
+ };
534
+ }
535
+ async deleteFile(workspaceRef, fileRef, subject = {}) {
536
+ await this.request('DELETE', `/${encodeRef(workspaceRef)}/files/${encodeFileRef(fileRef)}`, subject);
537
+ }
538
+ /**
539
+ * Get raw metadata for one file (`POST /workspaces/meta`).
540
+ */
541
+ async meta(workspaceRef, fileRef, subject = {}) {
542
+ return await this.request('POST', '/meta', subject, { workspace: workspaceRef, path: fileRef });
543
+ }
544
+ /**
545
+ * Write every Markdown projection for a workspace to
546
+ * `<outputDir>/<workspace_slug>/<path>.md` and return the written paths.
547
+ */
548
+ async syncManifest(workspaceRef, outputDir, subject = {}, options = {}) {
549
+ // Node-only: keep this module browser-importable by loading fs/path lazily
550
+ // with an opaque specifier so browser bundles skip these imports.
551
+ const fs = await loadNodeBuiltin('node:fs');
552
+ const path = await loadNodeBuiltin('node:path');
553
+ const manifest = await this.manifest(workspaceRef, subject);
554
+ const workspaceRoot = path.join(outputDir, manifest.workspaceSlug);
555
+ const written = [];
556
+ for (const markdownFile of manifest.markdownFiles) {
557
+ if (!markdownFile || typeof markdownFile !== 'object')
558
+ continue;
559
+ if (options.readyOnly && markdownFile.state !== 'processed')
560
+ continue;
561
+ const markdownPath = normalizePosixPath(String(markdownFile.path || ''));
562
+ if (!markdownPath) {
563
+ throw new Error('Workspace manifest markdown entry is missing a path');
564
+ }
565
+ const target = path.resolve(workspaceRoot, ...`${markdownPath}.md`.split('/').filter(Boolean));
566
+ const rootResolved = path.resolve(workspaceRoot);
567
+ if (target !== rootResolved && !target.startsWith(rootResolved + path.sep)) {
568
+ throw new Error(`Unsafe markdown path: ${markdownPath}.md`);
569
+ }
570
+ let body;
571
+ try {
572
+ const response = await requestWithRetry({
573
+ method: 'POST',
574
+ url: `${this.apiBase}/tomd`,
575
+ headers: this.headers(subject),
576
+ body: { workspace: workspaceRef, path: markdownFile.path, index: 1 },
577
+ retries: options.readyOnly ? 1 : 3,
578
+ timeout: this.timeout,
579
+ });
580
+ body = new TextDecoder().decode(await handleBytesResponse(response));
581
+ }
582
+ catch (error) {
583
+ if (options.readyOnly &&
584
+ error instanceof APIError &&
585
+ error.statusCode === 404 &&
586
+ String(error.detail ?? '').toLowerCase().includes('workspace markdown not found')) {
587
+ continue;
588
+ }
589
+ throw error;
590
+ }
591
+ fs.mkdirSync(path.dirname(target), { recursive: true });
592
+ fs.writeFileSync(target, body, 'utf8');
593
+ written.push(target);
594
+ }
595
+ return written;
596
+ }
597
+ /**
598
+ * Sync every shared Markdown collection to `<outputDir>/<slug>/...`.
599
+ * Returns a map from workspace slug to written file paths.
600
+ */
601
+ async syncAll(outputDir, subject = {}, options = {}) {
602
+ const synced = {};
603
+ for (const workspace of await this.list(subject)) {
604
+ synced[workspace.slug] = await this.syncManifest(workspace.id, outputDir, subject, options);
605
+ }
606
+ return synced;
607
+ }
608
+ async markdownFile(workspaceRef, fileRef, subject = {}) {
609
+ const manifest = await this.manifest(workspaceRef, subject);
610
+ const markdownFile = findMarkdownFile(manifest, fileRef);
611
+ const response = await requestWithRetry({
612
+ method: 'POST',
613
+ url: `${this.apiBase}/tomd`,
614
+ headers: this.headers(subject),
615
+ body: { workspace: workspaceRef, path: markdownFile.path || fileRef, index: 1 },
616
+ timeout: this.timeout,
617
+ });
618
+ const bytes = await handleBytesResponse(response);
619
+ return { markdownFile, markdown: new TextDecoder().decode(bytes) };
620
+ }
621
+ /**
622
+ * `markdownFile` with the `/tomd` document parsed: typed frontmatter plus
623
+ * the body (and `contentBody`, the body minus the sections the pipeline
624
+ * generates from the frontmatter). `markdownFile()` stays the raw-string
625
+ * variant.
626
+ */
627
+ async tomdDocument(workspaceRef, fileRef, subject = {}) {
628
+ const { markdownFile, markdown } = await this.markdownFile(workspaceRef, fileRef, subject);
629
+ return { markdownFile, markdown, ...parseWorkspaceTomd(markdown) };
630
+ }
631
+ }
632
+ function findMarkdownFile(manifest, fileRef) {
633
+ const normalizedRef = normalizePosixPath(fileRef);
634
+ for (const markdownFile of manifest.markdownFiles) {
635
+ if (!markdownFile || typeof markdownFile !== 'object')
636
+ continue;
637
+ if (fileRef === String(markdownFile.file_id || '')) {
638
+ return markdownFile;
639
+ }
640
+ if (normalizedRef === normalizePosixPath(String(markdownFile.path || ''))) {
641
+ return markdownFile;
642
+ }
643
+ }
644
+ throw new Error(`Shared knowledge Markdown file not found for ${fileRef}`);
645
+ }
646
+ function normalizePosixPath(path) {
647
+ return path.trim().replace(/\\/g, '/').replace(/^\.\/+/, '');
648
+ }
649
+ function fileNameFromPath(path) {
650
+ const normalized = normalizePosixPath(path).replace(/^\/+/, '').replace(/\/+$/, '');
651
+ return normalized.split('/').filter(Boolean).at(-1) || normalized || 'file';
652
+ }
653
+ //# sourceMappingURL=workspaces.js.map