@dbx-tools/shared-lakebox 0.0.0-stage → 0.9.132
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 +87 -2
- package/index.ts +12 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +10 -0
- package/lib/src/client.d.ts +37 -0
- package/lib/src/client.js +49 -0
- package/lib/src/schemas.d.ts +334 -0
- package/lib/src/schemas.js +367 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +84 -4
- package/src/client.ts +64 -0
- package/src/schemas.ts +465 -0
package/src/schemas.ts
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const lakeboxStatusSchema = z
|
|
4
|
+
.enum(["missing", "unknown", "starting", "running", "stopping", "stopped", "error"])
|
|
5
|
+
.describe(
|
|
6
|
+
"Normalized Lakebox lifecycle state. missing means the app Lakebox has not been created yet; it is created on first use.",
|
|
7
|
+
);
|
|
8
|
+
export type LakeboxStatus = z.infer<typeof lakeboxStatusSchema>;
|
|
9
|
+
|
|
10
|
+
export const lakeboxUserSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
hash: z
|
|
13
|
+
.string()
|
|
14
|
+
.regex(/^[0-9a-z]+$/)
|
|
15
|
+
.describe("FNV-1a hash of the authenticated identity; the only user routing key."),
|
|
16
|
+
name: z.string().min(1).describe("Locked, non-sudo Linux account that runs the user's work."),
|
|
17
|
+
uid: z.number().int().min(1).max(65535).describe("Deterministic Linux UID of the account."),
|
|
18
|
+
home: z.string().startsWith("/").describe("Stable home path, a symlink to persistent storage."),
|
|
19
|
+
routePrefix: z
|
|
20
|
+
.string()
|
|
21
|
+
.startsWith("/")
|
|
22
|
+
.describe("AppKit route prefix beneath which this user's services are exposed."),
|
|
23
|
+
})
|
|
24
|
+
.strict()
|
|
25
|
+
.describe("The Lakebox account derived from the current authenticated user.");
|
|
26
|
+
export type LakeboxUser = z.infer<typeof lakeboxUserSchema>;
|
|
27
|
+
|
|
28
|
+
/** Built-in services every user can start without a definition. */
|
|
29
|
+
export const LAKEBOX_BUILT_IN_SERVICES = [
|
|
30
|
+
{ name: "terminal", label: "Terminal" },
|
|
31
|
+
{ name: "desktop", label: "Desktop" },
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
export const lakeboxBuiltInServiceNameSchema = z
|
|
35
|
+
.enum(["terminal", "desktop"])
|
|
36
|
+
.describe("Built-in user service backed by a package-owned definition.");
|
|
37
|
+
export type LakeboxBuiltInServiceName = z.infer<typeof lakeboxBuiltInServiceNameSchema>;
|
|
38
|
+
|
|
39
|
+
/** Environment variables the runtime injects into every service. */
|
|
40
|
+
export const LAKEBOX_RESERVED_SERVICE_ENV = [
|
|
41
|
+
"SERVICE_NAME",
|
|
42
|
+
"SERVICE_PATH",
|
|
43
|
+
"SERVICE_PORT",
|
|
44
|
+
"PORT",
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
47
|
+
export const lakeboxServiceNameSchema = z
|
|
48
|
+
.string()
|
|
49
|
+
.regex(
|
|
50
|
+
/^[a-z][a-z0-9-]{0,30}$/,
|
|
51
|
+
"Use 1-31 lowercase letters, digits, or hyphens, starting with a letter.",
|
|
52
|
+
)
|
|
53
|
+
.describe(
|
|
54
|
+
"Service name, unique per user. It is the route segment and, with the user hash, derives the service port.",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
export const lakeboxServiceStateSchema = z
|
|
58
|
+
.enum(["stopped", "starting", "ready", "error"])
|
|
59
|
+
.describe("Current service state.");
|
|
60
|
+
export type LakeboxServiceState = z.infer<typeof lakeboxServiceStateSchema>;
|
|
61
|
+
|
|
62
|
+
export const lakeboxServiceKindSchema = z
|
|
63
|
+
.enum(["image", "terminal", "desktop"])
|
|
64
|
+
.describe(
|
|
65
|
+
"image runs an OCI image; terminal and desktop are built-in services that run the Lakebox host environment.",
|
|
66
|
+
);
|
|
67
|
+
export type LakeboxServiceKind = z.infer<typeof lakeboxServiceKindSchema>;
|
|
68
|
+
|
|
69
|
+
export const lakeboxServiceSchema = z
|
|
70
|
+
.object({
|
|
71
|
+
name: lakeboxServiceNameSchema,
|
|
72
|
+
kind: lakeboxServiceKindSchema,
|
|
73
|
+
image: z.string().optional().describe("OCI image reference for image services."),
|
|
74
|
+
openPath: z
|
|
75
|
+
.string()
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Configured entry path appended to the service route."),
|
|
78
|
+
state: lakeboxServiceStateSchema,
|
|
79
|
+
port: z
|
|
80
|
+
.number()
|
|
81
|
+
.int()
|
|
82
|
+
.min(1024)
|
|
83
|
+
.max(65535)
|
|
84
|
+
.describe("Loopback port derived from the user hash and service name."),
|
|
85
|
+
route: z
|
|
86
|
+
.boolean()
|
|
87
|
+
.describe("Whether AppKit exposes the service through an authenticated route."),
|
|
88
|
+
path: z.string().startsWith("/").describe("AppKit route path for the service."),
|
|
89
|
+
openUrl: z
|
|
90
|
+
.union([z.string().startsWith("/"), z.httpUrl()])
|
|
91
|
+
.optional()
|
|
92
|
+
.describe(
|
|
93
|
+
"AppKit URL that opens the service; REST responses use a path and agent tools resolve it against the browser app origin.",
|
|
94
|
+
),
|
|
95
|
+
error: z.string().optional().describe("Latest start or readiness failure."),
|
|
96
|
+
log: z
|
|
97
|
+
.string()
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("Bounded tail of the service log; omitted in agent listings."),
|
|
100
|
+
})
|
|
101
|
+
.strict()
|
|
102
|
+
.describe("One user-scoped Lakebox service.");
|
|
103
|
+
export type LakeboxService = z.infer<typeof lakeboxServiceSchema>;
|
|
104
|
+
|
|
105
|
+
export const lakeboxSummarySchema = z
|
|
106
|
+
.object({
|
|
107
|
+
supported: z.boolean().describe("Whether the workspace exposes the Databricks Lakebox API."),
|
|
108
|
+
auto: z
|
|
109
|
+
.boolean()
|
|
110
|
+
.describe("Whether commands, services, and routes start or create the Lakebox on demand."),
|
|
111
|
+
status: lakeboxStatusSchema,
|
|
112
|
+
id: z.string().optional().describe("Databricks Lakebox identifier, once created."),
|
|
113
|
+
name: z.string().min(1).describe("Product display label; never the upstream machine name."),
|
|
114
|
+
idleTimeoutSeconds: z
|
|
115
|
+
.number()
|
|
116
|
+
.int()
|
|
117
|
+
.positive()
|
|
118
|
+
.optional()
|
|
119
|
+
.describe("Platform idle timeout after which the Lakebox stops."),
|
|
120
|
+
user: lakeboxUserSchema,
|
|
121
|
+
services: z.array(lakeboxServiceSchema).describe("The current user's services."),
|
|
122
|
+
servicesStale: z
|
|
123
|
+
.boolean()
|
|
124
|
+
.describe(
|
|
125
|
+
"True when services come from the last successful controller observation rather than live Lakebox state.",
|
|
126
|
+
),
|
|
127
|
+
servicesObservedAt: z
|
|
128
|
+
.string()
|
|
129
|
+
.datetime()
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("Time of the last successful live service observation."),
|
|
132
|
+
canStart: z.boolean().describe("Whether the Lakebox can be started or created now."),
|
|
133
|
+
canStop: z.boolean().describe("Whether the Lakebox can be stopped now."),
|
|
134
|
+
})
|
|
135
|
+
.strict()
|
|
136
|
+
.describe("Secret-free summary of the app Lakebox for the current user.");
|
|
137
|
+
export type LakeboxSummary = z.infer<typeof lakeboxSummarySchema>;
|
|
138
|
+
|
|
139
|
+
const environmentSchema = z
|
|
140
|
+
.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), z.string())
|
|
141
|
+
.describe("Environment variables.");
|
|
142
|
+
|
|
143
|
+
export const lakeboxCommandRequestSchema = z
|
|
144
|
+
.object({
|
|
145
|
+
command: z
|
|
146
|
+
.string()
|
|
147
|
+
.min(1)
|
|
148
|
+
.regex(/^\S+$/, 'Command must be one executable name; use command "sh" for shell syntax.')
|
|
149
|
+
.describe(
|
|
150
|
+
'Executable name only. Put arguments in args. For shell syntax, use command "sh" with args ["-lc", "<script>"].',
|
|
151
|
+
),
|
|
152
|
+
args: z
|
|
153
|
+
.array(z.string())
|
|
154
|
+
.default([])
|
|
155
|
+
.describe("Individual arguments. Never concatenate the executable and its arguments."),
|
|
156
|
+
cwd: z
|
|
157
|
+
.string()
|
|
158
|
+
.optional()
|
|
159
|
+
.describe("Working directory on the Lakebox host. Defaults to the user's home."),
|
|
160
|
+
env: environmentSchema
|
|
161
|
+
.optional()
|
|
162
|
+
.describe(
|
|
163
|
+
"Non-authentication environment overlay. Databricks credentials and SSH agent variables are rejected.",
|
|
164
|
+
),
|
|
165
|
+
timeoutMs: z.number().int().positive().max(3_600_000).optional().describe("Command timeout."),
|
|
166
|
+
})
|
|
167
|
+
.strict()
|
|
168
|
+
.superRefine((request, context) => rejectCredentialEnvKeys(request.env, context))
|
|
169
|
+
.describe("Command executed as the current user's Lakebox account.");
|
|
170
|
+
export type LakeboxCommandRequest = z.input<typeof lakeboxCommandRequestSchema>;
|
|
171
|
+
|
|
172
|
+
export const lakeboxCommandResultSchema = z
|
|
173
|
+
.object({
|
|
174
|
+
success: z.boolean().describe("Whether the command exited successfully."),
|
|
175
|
+
exitCode: z.number().int().describe("Remote exit code."),
|
|
176
|
+
stdout: z.string().describe("Captured standard output."),
|
|
177
|
+
stderr: z.string().describe("Captured standard error."),
|
|
178
|
+
executionTimeMs: z.number().nonnegative().describe("Elapsed execution time."),
|
|
179
|
+
timedOut: z.boolean().optional().describe("Whether timeout terminated the command."),
|
|
180
|
+
command: z.string().describe("Executed command."),
|
|
181
|
+
args: z.array(z.string()).describe("Executed arguments."),
|
|
182
|
+
})
|
|
183
|
+
.strict()
|
|
184
|
+
.describe("Lakebox command execution result.");
|
|
185
|
+
export type LakeboxCommandResult = z.infer<typeof lakeboxCommandResultSchema>;
|
|
186
|
+
|
|
187
|
+
export const lakeboxServiceMountSchema = z
|
|
188
|
+
.object({
|
|
189
|
+
source: z
|
|
190
|
+
.string()
|
|
191
|
+
.min(1)
|
|
192
|
+
.describe(
|
|
193
|
+
"Host directory relative to the user's home, such as data. It is created when missing. Absolute paths and parent traversal are rejected.",
|
|
194
|
+
),
|
|
195
|
+
target: z
|
|
196
|
+
.string()
|
|
197
|
+
.startsWith("/")
|
|
198
|
+
.describe("Absolute path inside the container, such as /example."),
|
|
199
|
+
readOnly: z.boolean().default(false).describe("Mount read-only inside the container."),
|
|
200
|
+
})
|
|
201
|
+
.strict()
|
|
202
|
+
.refine((mount) => !mount.source.startsWith("/") && !mount.source.split("/").includes(".."), {
|
|
203
|
+
message: "Mount sources must stay inside the user's home.",
|
|
204
|
+
path: ["source"],
|
|
205
|
+
})
|
|
206
|
+
.describe("Bind mount from the user's persistent home into the container.");
|
|
207
|
+
export type LakeboxServiceMount = z.input<typeof lakeboxServiceMountSchema>;
|
|
208
|
+
|
|
209
|
+
export const createLakeboxServiceRequestSchema = z
|
|
210
|
+
.object({
|
|
211
|
+
name: lakeboxServiceNameSchema,
|
|
212
|
+
image: z
|
|
213
|
+
.string()
|
|
214
|
+
.min(1)
|
|
215
|
+
.regex(/^\S+$/)
|
|
216
|
+
.describe(
|
|
217
|
+
"OCI image reference pulled anonymously, such as docker.io/library/python:3.12-alpine.",
|
|
218
|
+
),
|
|
219
|
+
command: z
|
|
220
|
+
.array(z.string())
|
|
221
|
+
.min(1)
|
|
222
|
+
.optional()
|
|
223
|
+
.describe(
|
|
224
|
+
'Complete argv that replaces the image entrypoint and command. Tokens are passed literally; there is no shell parsing unless the first tokens are a shell such as ["sh", "-c", "..."]. Omit to run the image\'s own entrypoint and command.',
|
|
225
|
+
),
|
|
226
|
+
env: environmentSchema
|
|
227
|
+
.default({})
|
|
228
|
+
.describe(
|
|
229
|
+
"Container environment. SERVICE_NAME, SERVICE_PATH, SERVICE_PORT, and PORT are injected and reserved; Databricks credentials are rejected.",
|
|
230
|
+
),
|
|
231
|
+
workingDirectory: z
|
|
232
|
+
.string()
|
|
233
|
+
.startsWith("/")
|
|
234
|
+
.optional()
|
|
235
|
+
.describe("Working directory inside the container. Defaults to the image working directory."),
|
|
236
|
+
mounts: z
|
|
237
|
+
.array(lakeboxServiceMountSchema)
|
|
238
|
+
.default([])
|
|
239
|
+
.describe("Bind mounts from the user's home. Host file permissions still apply."),
|
|
240
|
+
route: z
|
|
241
|
+
.boolean()
|
|
242
|
+
.default(true)
|
|
243
|
+
.describe(
|
|
244
|
+
"Expose the service through the user's authenticated AppKit route. Routed services must listen on 127.0.0.1:$SERVICE_PORT; there is no Docker-style port publishing.",
|
|
245
|
+
),
|
|
246
|
+
openPath: z
|
|
247
|
+
.string()
|
|
248
|
+
.optional()
|
|
249
|
+
.describe("Entry path appended to the route, such as index.html?mode=1."),
|
|
250
|
+
stripPrefix: z
|
|
251
|
+
.boolean()
|
|
252
|
+
.default(true)
|
|
253
|
+
.describe(
|
|
254
|
+
"Remove the route prefix before forwarding. Set false only when the server is configured for the full SERVICE_PATH base path.",
|
|
255
|
+
),
|
|
256
|
+
pull: z
|
|
257
|
+
.enum(["missing", "always"])
|
|
258
|
+
.default("missing")
|
|
259
|
+
.describe("Pull the image only when absent, or always before starting."),
|
|
260
|
+
})
|
|
261
|
+
.strict()
|
|
262
|
+
.superRefine((request, context) => {
|
|
263
|
+
rejectCredentialEnvKeys(request.env, context);
|
|
264
|
+
for (const name of LAKEBOX_RESERVED_SERVICE_ENV) {
|
|
265
|
+
if (name in request.env) {
|
|
266
|
+
context.addIssue({
|
|
267
|
+
code: "custom",
|
|
268
|
+
path: ["env", name],
|
|
269
|
+
message: `${name} is injected by the Lakebox runtime and cannot be set.`,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (LAKEBOX_BUILT_IN_SERVICES.some((service) => service.name === request.name)) {
|
|
274
|
+
context.addIssue({
|
|
275
|
+
code: "custom",
|
|
276
|
+
path: ["name"],
|
|
277
|
+
message: `${request.name} is a built-in service name.`,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
.describe(
|
|
282
|
+
"Create or replace a user service that runs an OCI image detached under the Lakebox runtime.",
|
|
283
|
+
);
|
|
284
|
+
export type CreateLakeboxServiceRequest = z.input<typeof createLakeboxServiceRequestSchema>;
|
|
285
|
+
export type LakeboxServiceDefinition = z.output<typeof createLakeboxServiceRequestSchema>;
|
|
286
|
+
|
|
287
|
+
export const lakeboxServiceActionSchema = z
|
|
288
|
+
.object({ name: lakeboxServiceNameSchema })
|
|
289
|
+
.strict()
|
|
290
|
+
.describe("Request that targets one of the current user's services.");
|
|
291
|
+
export type LakeboxServiceAction = z.infer<typeof lakeboxServiceActionSchema>;
|
|
292
|
+
|
|
293
|
+
/** Default number of service log lines returned by bounded log reads. */
|
|
294
|
+
export const LAKEBOX_LOG_DEFAULT_MAX_LINES = 200;
|
|
295
|
+
/** Default service log character cap returned by bounded log reads. */
|
|
296
|
+
export const LAKEBOX_LOG_DEFAULT_MAX_CHARS = 16_000;
|
|
297
|
+
|
|
298
|
+
export const lakeboxServiceLogsRequestSchema = lakeboxServiceActionSchema
|
|
299
|
+
.extend({
|
|
300
|
+
maxLines: z.coerce
|
|
301
|
+
.number()
|
|
302
|
+
.int()
|
|
303
|
+
.min(1)
|
|
304
|
+
.max(1_000)
|
|
305
|
+
.default(LAKEBOX_LOG_DEFAULT_MAX_LINES)
|
|
306
|
+
.describe("Maximum number of trailing log lines."),
|
|
307
|
+
maxChars: z.coerce
|
|
308
|
+
.number()
|
|
309
|
+
.int()
|
|
310
|
+
.min(1)
|
|
311
|
+
.max(64_000)
|
|
312
|
+
.default(LAKEBOX_LOG_DEFAULT_MAX_CHARS)
|
|
313
|
+
.describe("Maximum number of trailing log characters."),
|
|
314
|
+
})
|
|
315
|
+
.strict()
|
|
316
|
+
.describe("Bounded log request for one service.");
|
|
317
|
+
export type LakeboxServiceLogsRequest = z.input<typeof lakeboxServiceLogsRequestSchema>;
|
|
318
|
+
|
|
319
|
+
export const lakeboxServiceLogsResponseSchema = z
|
|
320
|
+
.object({ log: z.string().describe("Bounded trailing service log, including image pulls.") })
|
|
321
|
+
.strict()
|
|
322
|
+
.describe("Bounded service log response.");
|
|
323
|
+
export type LakeboxServiceLogsResponse = z.infer<typeof lakeboxServiceLogsResponseSchema>;
|
|
324
|
+
|
|
325
|
+
export const lakeboxServicesResponseSchema = z
|
|
326
|
+
.object({ services: z.array(lakeboxServiceSchema).describe("The current user's services.") })
|
|
327
|
+
.strict()
|
|
328
|
+
.describe("Service listing response.");
|
|
329
|
+
export type LakeboxServicesResponse = z.infer<typeof lakeboxServicesResponseSchema>;
|
|
330
|
+
|
|
331
|
+
export const lakeboxServiceResponseSchema = z
|
|
332
|
+
.object({ service: lakeboxServiceSchema })
|
|
333
|
+
.strict()
|
|
334
|
+
.describe("Single service response.");
|
|
335
|
+
export type LakeboxServiceResponse = z.infer<typeof lakeboxServiceResponseSchema>;
|
|
336
|
+
|
|
337
|
+
export const removeLakeboxServiceResponseSchema = z
|
|
338
|
+
.object({ removed: z.boolean().describe("Whether a saved service was removed.") })
|
|
339
|
+
.strict()
|
|
340
|
+
.describe("Service removal response.");
|
|
341
|
+
export type RemoveLakeboxServiceResponse = z.infer<typeof removeLakeboxServiceResponseSchema>;
|
|
342
|
+
|
|
343
|
+
export const lakeboxMetricsSnapshotSchema = z
|
|
344
|
+
.object({
|
|
345
|
+
at: z.string().datetime().describe("Sample time."),
|
|
346
|
+
cpu: z
|
|
347
|
+
.object({
|
|
348
|
+
cores: z.number().int().positive().describe("Online CPU count."),
|
|
349
|
+
usedPercent: z
|
|
350
|
+
.number()
|
|
351
|
+
.min(0)
|
|
352
|
+
.max(100)
|
|
353
|
+
.describe("CPU busy percentage since the last sample."),
|
|
354
|
+
load1: z.number().nonnegative().describe("One-minute load average."),
|
|
355
|
+
})
|
|
356
|
+
.strict()
|
|
357
|
+
.describe("CPU usage."),
|
|
358
|
+
memory: z
|
|
359
|
+
.object({
|
|
360
|
+
totalBytes: z.number().nonnegative().describe("Total memory."),
|
|
361
|
+
usedBytes: z.number().nonnegative().describe("Memory in use, excluding reclaimable cache."),
|
|
362
|
+
})
|
|
363
|
+
.strict()
|
|
364
|
+
.describe("Memory usage."),
|
|
365
|
+
storage: z
|
|
366
|
+
.object({
|
|
367
|
+
totalBytes: z.number().nonnegative().describe("Persistent home volume size."),
|
|
368
|
+
usedBytes: z.number().nonnegative().describe("Persistent home volume usage."),
|
|
369
|
+
})
|
|
370
|
+
.strict()
|
|
371
|
+
.describe("Persistent storage usage."),
|
|
372
|
+
network: z
|
|
373
|
+
.object({
|
|
374
|
+
rxBytesPerSecond: z.number().nonnegative().describe("Inbound network throughput."),
|
|
375
|
+
txBytesPerSecond: z.number().nonnegative().describe("Outbound network throughput."),
|
|
376
|
+
})
|
|
377
|
+
.strict()
|
|
378
|
+
.describe("Network throughput on the primary interface."),
|
|
379
|
+
})
|
|
380
|
+
.strict()
|
|
381
|
+
.describe("Machine-level Lakebox resource sample. Never includes process information.");
|
|
382
|
+
export type LakeboxMetricsSnapshot = z.infer<typeof lakeboxMetricsSnapshotSchema>;
|
|
383
|
+
|
|
384
|
+
export const lakeboxMetricsResponseSchema = z
|
|
385
|
+
.object({
|
|
386
|
+
status: lakeboxStatusSchema,
|
|
387
|
+
series: z
|
|
388
|
+
.array(lakeboxMetricsSnapshotSchema)
|
|
389
|
+
.describe("Recent samples, oldest first. Empty while the Lakebox is not running."),
|
|
390
|
+
})
|
|
391
|
+
.strict()
|
|
392
|
+
.describe("Lakebox metrics response.");
|
|
393
|
+
export type LakeboxMetricsResponse = z.infer<typeof lakeboxMetricsResponseSchema>;
|
|
394
|
+
|
|
395
|
+
const readTool = { appkitTool: { agentTool: true, effect: "read", requiresUserContext: true } };
|
|
396
|
+
const writeTool = { appkitTool: { agentTool: true, effect: "write", requiresUserContext: true } };
|
|
397
|
+
|
|
398
|
+
export const getLakeboxOperationSchema = z
|
|
399
|
+
.object({})
|
|
400
|
+
.strict()
|
|
401
|
+
.describe(
|
|
402
|
+
"Return the app Lakebox status, the current user's Lakebox account, and that user's services. This never creates the Lakebox.",
|
|
403
|
+
)
|
|
404
|
+
.meta(readTool);
|
|
405
|
+
|
|
406
|
+
export const executeLakeboxCommandOperationSchema = lakeboxCommandRequestSchema
|
|
407
|
+
.describe(
|
|
408
|
+
"Run a command on the Lakebox host as the current user's locked, non-sudo account, starting the Lakebox when needed. Use services, not background commands, for long-running servers.",
|
|
409
|
+
)
|
|
410
|
+
.meta(writeTool);
|
|
411
|
+
|
|
412
|
+
export const listLakeboxServicesOperationSchema = z
|
|
413
|
+
.object({})
|
|
414
|
+
.strict()
|
|
415
|
+
.describe("List the current user's services with state, port, and route. Logs are omitted.")
|
|
416
|
+
.meta(readTool);
|
|
417
|
+
|
|
418
|
+
export const createLakeboxServiceOperationSchema = createLakeboxServiceRequestSchema
|
|
419
|
+
.describe(
|
|
420
|
+
"Create or replace a user service from an OCI image, start it detached, and wait until it is ready or failed. This is a constrained container API, not the Docker CLI: there is no daemon, port publishing, privileged mode, or device access. Routed services listen on 127.0.0.1:$SERVICE_PORT and are reached at the returned openUrl. Services restart from their saved definition when the Lakebox starts; process memory is not resumed.",
|
|
421
|
+
)
|
|
422
|
+
.meta(writeTool);
|
|
423
|
+
|
|
424
|
+
export const startLakeboxServiceOperationSchema = lakeboxServiceActionSchema
|
|
425
|
+
.describe(
|
|
426
|
+
"Start a saved service or a built-in terminal or desktop and wait until it is ready or failed.",
|
|
427
|
+
)
|
|
428
|
+
.meta({ appkitTool: { ...writeTool.appkitTool, idempotent: true } });
|
|
429
|
+
|
|
430
|
+
export const stopLakeboxServiceOperationSchema = lakeboxServiceActionSchema
|
|
431
|
+
.describe("Stop one running service without removing its definition.")
|
|
432
|
+
.meta({ appkitTool: { ...writeTool.appkitTool, idempotent: true } });
|
|
433
|
+
|
|
434
|
+
export const removeLakeboxServiceOperationSchema = lakeboxServiceActionSchema
|
|
435
|
+
.describe(
|
|
436
|
+
"Stop and delete one saved service definition and its image root filesystem. Mounted home directories are preserved.",
|
|
437
|
+
)
|
|
438
|
+
.meta({
|
|
439
|
+
appkitTool: {
|
|
440
|
+
agentTool: true,
|
|
441
|
+
effect: "destructive",
|
|
442
|
+
approval: true,
|
|
443
|
+
requiresUserContext: true,
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
export const getLakeboxServiceLogsOperationSchema = lakeboxServiceLogsRequestSchema
|
|
448
|
+
.describe("Read a bounded tail of one service log, including image pull and readiness output.")
|
|
449
|
+
.meta(readTool);
|
|
450
|
+
|
|
451
|
+
function rejectCredentialEnvKeys(
|
|
452
|
+
env: Readonly<Record<string, string>> | undefined,
|
|
453
|
+
context: z.RefinementCtx,
|
|
454
|
+
): void {
|
|
455
|
+
for (const name of Object.keys(env ?? {})) {
|
|
456
|
+
if (name.startsWith("DATABRICKS_") || name === "SSH_AUTH_SOCK") {
|
|
457
|
+
context.addIssue({
|
|
458
|
+
code: "custom",
|
|
459
|
+
path: ["env", name],
|
|
460
|
+
message:
|
|
461
|
+
"Environment overlays cannot restore Databricks or SSH agent credentials; use a dedicated app-mediated capability.",
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|