@zgeoff/imp-client 0.16.0 → 0.18.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.
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { RPCLink } from "@orpc/client/fetch";
3
3
  import * as z from "zod";
4
4
  //#endregion
5
5
  //#region src/check-server.ts
6
- const CLIENT_VERSION = "0.16.0";
6
+ const CLIENT_VERSION = "0.18.0";
7
7
  async function checkServer(rpc) {
8
8
  const info = await rpc.system.info();
9
9
  return {
@@ -28,6 +28,137 @@ function parseVersion(version) {
28
28
  //#endregion
29
29
  //#region ../api/src/name-schema.ts
30
30
  const NameSchema = z.string().regex(/^[a-z][a-z0-9-]{0,30}$/, "must be a lowercase letter followed by up to 30 lowercase letters, digits or hyphens");
31
+ z.enum(["tailnet", "public"]);
32
+ const PublicAuthSchema = z.enum([
33
+ "none",
34
+ "token",
35
+ "basic"
36
+ ]);
37
+ const BasicUserSchema = z.string().regex(/^[!-9;-~]{1,64}$/, "must be 1 to 64 printable characters, without a colon");
38
+ z.object({
39
+ name: NameSchema,
40
+ auth: PublicAuthSchema.default("token"),
41
+ user: BasicUserSchema.optional()
42
+ }).refine((input) => input.auth === "basic" || input.user === void 0, {
43
+ message: "only basic auth takes a user",
44
+ path: ["user"]
45
+ });
46
+ z.object({
47
+ url: z.url(),
48
+ auth: PublicAuthSchema,
49
+ user: z.string().nullable(),
50
+ credential: z.string().nullable(),
51
+ warning: z.string().optional()
52
+ });
53
+ z.string().regex(/^[\w.:-]{1,64}$/, "must be 1–64 letters, digits, dots, underscores, colons or hyphens");
54
+ z.int().min(10).max(3600);
55
+ const LeaseOwnerSchema = z.object({
56
+ principal: z.string(),
57
+ display: z.string(),
58
+ label: z.string()
59
+ });
60
+ const LeaseSchema = z.object({
61
+ name: NameSchema,
62
+ owner: LeaseOwnerSchema,
63
+ until: z.date().nullable()
64
+ });
65
+ const LeaseSummarySchema = z.object({
66
+ leases: z.array(LeaseSchema).readonly(),
67
+ otherCount: z.int().nonnegative()
68
+ });
69
+ //#endregion
70
+ //#region ../api/src/move-schema.ts
71
+ const MoveStateSchema = z.enum([
72
+ "sending",
73
+ "moved",
74
+ "receiving"
75
+ ]);
76
+ const PeerUrlSchema = z.url({ protocol: /^https?$/ });
77
+ z.object({
78
+ bytes: z.int().nonnegative(),
79
+ checkpoints: z.int().nonnegative()
80
+ });
81
+ z.object({
82
+ ticket: z.string(),
83
+ expiresAt: z.date(),
84
+ peerUrl: PeerUrlSchema
85
+ });
86
+ z.object({
87
+ state: MoveStateSchema.nullable(),
88
+ peer: z.string().nullable(),
89
+ sentBytes: z.int().nonnegative(),
90
+ totalBytes: z.int().nonnegative(),
91
+ isDone: z.boolean(),
92
+ error: z.string().nullable()
93
+ });
94
+ //#endregion
95
+ //#region ../api/src/imp-schema.ts
96
+ const ImpStateSchema = z.enum([
97
+ "creating",
98
+ "running",
99
+ "sleeping",
100
+ "stopped",
101
+ "error"
102
+ ]);
103
+ const OutdatedPartSchema = z.enum([
104
+ "firecracker",
105
+ "kernel",
106
+ "agent",
107
+ "impd",
108
+ "ipv6"
109
+ ]);
110
+ const ImpResourcesSchema = z.object({
111
+ wakeCount: z.int().nonnegative(),
112
+ awakeMs: z.int().nonnegative(),
113
+ sample: z.object({
114
+ measuredAt: z.date(),
115
+ since: z.date(),
116
+ cpuPercent: z.number().nonnegative().optional(),
117
+ cpuThrottledMs: z.int().nonnegative(),
118
+ netRxBytes: z.int().nonnegative(),
119
+ netTxBytes: z.int().nonnegative()
120
+ }).optional()
121
+ });
122
+ z.object({
123
+ id: z.string(),
124
+ name: NameSchema,
125
+ image: NameSchema,
126
+ state: ImpStateSchema,
127
+ vcpus: z.int().positive(),
128
+ memoryMib: z.int().positive(),
129
+ diskMib: z.int().positive(),
130
+ diskUsage: z.object({
131
+ exclusiveBytes: z.int().nonnegative(),
132
+ sharedBytes: z.int().nonnegative(),
133
+ measuredAt: z.date(),
134
+ isPartial: z.boolean(),
135
+ isUpperBound: z.boolean()
136
+ }).optional(),
137
+ ip: z.ipv4(),
138
+ slot: z.int().nonnegative(),
139
+ port: z.int().positive(),
140
+ httpPort: z.int().positive(),
141
+ url: z.url(),
142
+ public: z.object({ auth: PublicAuthSchema }).optional(),
143
+ createdAt: z.date(),
144
+ lastActiveAt: z.date(),
145
+ ramMib: z.int().nonnegative().optional(),
146
+ rssMib: z.int().nonnegative().optional(),
147
+ sleptAt: z.date().optional(),
148
+ holdUntil: z.date().optional(),
149
+ leases: LeaseSummarySchema.optional(),
150
+ error: z.string().optional(),
151
+ sessions: z.int().nonnegative().optional(),
152
+ coldBootReason: z.string().optional(),
153
+ agentSilentSince: z.date().optional(),
154
+ outdated: z.array(OutdatedPartSchema).readonly().optional(),
155
+ cpu: z.object({
156
+ limit: z.number().positive().nullable(),
157
+ weight: z.int()
158
+ }).optional(),
159
+ resources: ImpResourcesSchema.optional(),
160
+ move: MoveStateSchema.optional()
161
+ });
31
162
  //#endregion
32
163
  //#region ../api/src/image-schema.ts
33
164
  const ImageSourceSchema = z.enum(["oci", "imp"]);
@@ -54,6 +185,67 @@ const ImageBuildErrorSchema = z.object({
54
185
  message: z.string()
55
186
  });
56
187
  //#endregion
188
+ //#region ../api/src/session-output-schema.ts
189
+ const ExecutionGenerationSchema = z.string().regex(/^[0-9a-f]{32}$/);
190
+ const OffsetSchema = z.int().nonnegative();
191
+ const ColdBootCauseSchema = z.enum([
192
+ "start",
193
+ "wake_fallback",
194
+ "watchdog",
195
+ "restore",
196
+ "recovery",
197
+ "unknown"
198
+ ]);
199
+ const ColdBootSchema = z.object({
200
+ bootId: z.string(),
201
+ cause: ColdBootCauseSchema,
202
+ at: z.iso.datetime()
203
+ }).readonly();
204
+ const ColdBootsSchema = z.array(ColdBootSchema).max(4).readonly();
205
+ const PreviousGenerationSchema = z.object({
206
+ executionGeneration: ExecutionGenerationSchema,
207
+ end: OffsetSchema,
208
+ exitCode: z.int().nullable()
209
+ }).readonly();
210
+ const ResumeFromSchema = z.object({
211
+ executionGeneration: ExecutionGenerationSchema,
212
+ offset: OffsetSchema
213
+ }).readonly();
214
+ const ResumeResultSchema = z.discriminatedUnion("kind", [
215
+ z.object({ kind: z.literal("exact") }),
216
+ z.object({
217
+ kind: z.literal("gap"),
218
+ from: OffsetSchema,
219
+ to: OffsetSchema
220
+ }),
221
+ z.object({
222
+ kind: z.literal("generation_changed"),
223
+ executionGeneration: ExecutionGenerationSchema,
224
+ firstOffset: OffsetSchema
225
+ })
226
+ ]).readonly();
227
+ const SessionOutputSchema = z.discriminatedUnion("continuity", [z.object({ continuity: z.literal("none") }), z.object({
228
+ continuity: z.literal("offsets"),
229
+ bootId: z.string(),
230
+ executionGeneration: ExecutionGenerationSchema,
231
+ bufferStart: OffsetSchema,
232
+ end: OffsetSchema,
233
+ offset: OffsetSchema,
234
+ prelude: OffsetSchema,
235
+ coldBoots: ColdBootsSchema,
236
+ previous: PreviousGenerationSchema.optional(),
237
+ resume: ResumeResultSchema.optional()
238
+ })]).readonly();
239
+ const NoSessionDataSchema = z.object({
240
+ bootId: z.string(),
241
+ coldBoots: ColdBootsSchema,
242
+ previous: PreviousGenerationSchema.optional()
243
+ }).readonly();
244
+ const InvalidResumeDataSchema = z.object({
245
+ end: OffsetSchema,
246
+ bufferStart: OffsetSchema
247
+ }).readonly();
248
+ //#endregion
57
249
  //#region ../api/src/session-schema.ts
58
250
  const SessionNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{0,31}$/, "must be a lowercase letter or digit followed by up to 31 lowercase letters, digits or hyphens");
59
251
  const SessionExitSchema = z.object({
@@ -69,7 +261,12 @@ z.object({
69
261
  cols: z.int().positive(),
70
262
  rows: z.int().positive(),
71
263
  startedAt: z.date(),
72
- exit: SessionExitSchema.optional()
264
+ exit: SessionExitSchema.optional(),
265
+ continuity: z.enum(["none", "offsets"]).optional(),
266
+ executionGeneration: ExecutionGenerationSchema.optional(),
267
+ bootId: z.string().optional(),
268
+ end: z.int().nonnegative().optional(),
269
+ endObservedAt: z.date().optional()
73
270
  });
74
271
  //#endregion
75
272
  //#region ../api/src/exec-protocol.ts
@@ -92,23 +289,33 @@ const ExecStartMessageSchema = z.object({
92
289
  rows: DimensionSchema.optional(),
93
290
  session: SessionNameSchema.optional(),
94
291
  tool: z.enum(["tar"]).optional(),
95
- killGraceMs: z.int().min(1).max(6e4).optional()
292
+ outer: z.boolean().optional(),
293
+ killGraceMs: z.int().min(1).max(6e4).optional(),
294
+ resumeFrom: ResumeFromSchema.optional()
96
295
  }).refine((start) => start.session === void 0 || start.tty, {
97
296
  message: "a session needs a tty",
98
297
  path: ["tty"]
298
+ }).refine((start) => start.resumeFrom === void 0 || start.session !== void 0, {
299
+ message: "only a session resumes",
300
+ path: ["resumeFrom"]
99
301
  }).refine((start) => start.killGraceMs === void 0 || !start.tty, {
100
302
  message: "a tty exec takes no kill grace",
101
303
  path: ["killGraceMs"]
102
304
  }).refine((start) => start.tool === void 0 || !start.tty && start.session === void 0 && start.env === void 0 && start.cwd === void 0, {
103
305
  message: "a tool takes no tty, session, env or cwd",
104
306
  path: ["tool"]
307
+ }).refine((start) => start.outer !== true || start.tool === void 0 && start.session === void 0, {
308
+ message: "an outer exec takes no tool or session",
309
+ path: ["outer"]
105
310
  });
106
311
  const ExecAttachMessageSchema = z.object({
107
312
  type: z.literal("attach"),
108
313
  name: NameSchema,
109
314
  session: SessionNameSchema,
110
315
  cols: DimensionSchema.optional(),
111
- rows: DimensionSchema.optional()
316
+ rows: DimensionSchema.optional(),
317
+ resumeFrom: ResumeFromSchema.optional(),
318
+ wake: z.boolean().optional()
112
319
  });
113
320
  z.discriminatedUnion("type", [
114
321
  ExecStartMessageSchema,
@@ -134,12 +341,14 @@ const ExecServerMessageSchema = z.discriminatedUnion("type", [
134
341
  pid: z.int().positive(),
135
342
  session: SessionNameSchema.optional(),
136
343
  created: z.boolean().optional(),
137
- groupKill: z.boolean().optional()
344
+ groupKill: z.boolean().optional(),
345
+ output: SessionOutputSchema.optional()
138
346
  }),
139
347
  z.object({
140
348
  type: z.literal("exit"),
141
349
  code: z.int().nullable(),
142
- signal: z.string().nullable()
350
+ signal: z.string().nullable(),
351
+ offset: z.int().nonnegative().optional()
143
352
  }),
144
353
  z.object({
145
354
  type: z.literal("detached"),
@@ -147,7 +356,8 @@ const ExecServerMessageSchema = z.discriminatedUnion("type", [
147
356
  "taken_over",
148
357
  "slow",
149
358
  "lost"
150
- ])
359
+ ]),
360
+ offset: z.int().nonnegative().optional()
151
361
  }),
152
362
  z.object({
153
363
  type: z.literal("stdin_ack"),
@@ -235,6 +445,102 @@ const TunnelServerMessageSchema = z.discriminatedUnion("type", [
235
445
  })
236
446
  ]);
237
447
  //#endregion
448
+ //#region ../api/src/define-errors.ts
449
+ function defineErrors(errors) {
450
+ return errors;
451
+ }
452
+ //#endregion
453
+ //#region ../api/src/imp-errors.ts
454
+ const ResourceKindSchema = z.enum([
455
+ "imp",
456
+ "image",
457
+ "checkpoint",
458
+ "session",
459
+ "service",
460
+ "secret",
461
+ "grant",
462
+ "backup",
463
+ "token",
464
+ "ssh-key",
465
+ "network"
466
+ ]);
467
+ const ResourceDataSchema = z.object({
468
+ kind: ResourceKindSchema,
469
+ name: z.string()
470
+ });
471
+ const ProtectedImpSchema = z.object({
472
+ name: NameSchema,
473
+ ramMib: z.int().nonnegative(),
474
+ leased: z.boolean(),
475
+ busy: z.boolean()
476
+ });
477
+ const IMP_ERRORS = defineErrors({
478
+ NOT_FOUND: {
479
+ message: "Not found",
480
+ data: ResourceDataSchema
481
+ },
482
+ CONFLICT: {
483
+ message: "Already exists",
484
+ data: ResourceDataSchema
485
+ },
486
+ FORBIDDEN: { message: "Not allowed" },
487
+ PRECONDITION_FAILED: { message: "Not possible on this host" },
488
+ RAM_BUDGET_EXCEEDED: {
489
+ message: "Not enough RAM budget, even after sleeping idle imps",
490
+ status: 503,
491
+ data: z.object({
492
+ budgetMib: z.int().nonnegative(),
493
+ usedMib: z.int().nonnegative(),
494
+ requestedMib: z.int().nonnegative(),
495
+ neededMib: z.int().nonnegative().optional(),
496
+ protected: z.array(ProtectedImpSchema).readonly().optional(),
497
+ protectedHidden: z.int().nonnegative().optional()
498
+ })
499
+ },
500
+ SERVICE_UNAVAILABLE: { message: "impd is stopping" },
501
+ INVALID_STATE: {
502
+ message: "The imp is not in a state that allows this",
503
+ status: 409,
504
+ data: z.object({
505
+ state: ImpStateSchema,
506
+ allowed: z.array(ImpStateSchema),
507
+ coldBoots: ColdBootsSchema.optional()
508
+ })
509
+ },
510
+ INVALID_RESUME: {
511
+ message: "The resume offset is past the end of the output",
512
+ status: 409,
513
+ data: InvalidResumeDataSchema
514
+ },
515
+ DISK_FULL: {
516
+ message: "Not enough free disk on the host",
517
+ status: 507,
518
+ data: z.object({
519
+ availableBytes: z.int().nonnegative(),
520
+ reserveBytes: z.int().nonnegative(),
521
+ requestedBytes: z.int().nonnegative()
522
+ })
523
+ },
524
+ LEASED: {
525
+ message: "The imp is leased",
526
+ status: 409,
527
+ data: LeaseSummarySchema
528
+ },
529
+ LEASE_NOT_HELD: {
530
+ message: "The caller holds no such lease",
531
+ status: 409
532
+ },
533
+ AGENT_OUTDATED: {
534
+ message: "The imp's agent is too old for this",
535
+ status: 409
536
+ },
537
+ MOVING: {
538
+ message: "The imp is moving between hosts",
539
+ status: 409,
540
+ data: z.object({ retryAfterS: z.int().positive() })
541
+ }
542
+ });
543
+ //#endregion
238
544
  //#region ../api/src/login-shell.ts
239
545
  const CONSOLE_SHELL = [
240
546
  "uid=$(id -u 2>/dev/null) || uid=0",
@@ -297,15 +603,6 @@ var ExecError = class extends Error {
297
603
  this.data = options.data;
298
604
  }
299
605
  };
300
- function toExecError(outcome) {
301
- if (outcome.kind === "failed") return new ExecError(outcome.code ?? "EXEC_ERROR", outcome.message, { data: outcome.data });
302
- if (outcome.kind === "unauthorized") return new ExecError("UNAUTHORIZED", outcome.ticketRefused === true ? "impd refused the exec ticket: it expired (30 s) or was used already" : "impd rejected the token");
303
- if (outcome.kind === "unreachable") return new ExecError("UNREACHABLE", `cannot reach impd (${outcome.detail})`);
304
- if (outcome.kind === "detached") return new ExecError("DETACHED", `detached from the session (${outcome.reason})`, { data: { reason: outcome.reason } });
305
- if (outcome.kind === "closed") return outcome.closeCode === 1012 ? new ExecError("RESTARTING", "impd is restarting") : new ExecError("CONNECTION_CLOSED", `exec connection closed (${outcome.reason})`);
306
- if (outcome.kind === "bad_message") return new ExecError("BAD_MESSAGE", `bad message from impd: ${outcome.detail}`);
307
- return new ExecError("LOCAL_ERROR", "an output handler threw", { cause: outcome.error });
308
- }
309
606
  //#endregion
310
607
  //#region src/exec/check-impd-access.ts
311
608
  const CHECK_TIMEOUT_MS = 5e3;
@@ -391,16 +688,19 @@ function openExecSession(options) {
391
688
  pid: message.pid,
392
689
  session: message.session ?? null,
393
690
  created: message.created ?? false,
394
- groupKill: message.groupKill ?? false
691
+ groupKill: message.groupKill ?? false,
692
+ output: message.output ?? { continuity: "none" }
395
693
  });
396
694
  } else if (message.type === "exit") resolveOutcome({
397
695
  kind: "exit",
398
696
  code: message.code,
399
- signal: message.signal
697
+ signal: message.signal,
698
+ ...message.offset !== void 0 && { offset: message.offset }
400
699
  });
401
700
  else if (message.type === "detached") resolveOutcome({
402
701
  kind: "detached",
403
- reason: message.reason
702
+ reason: message.reason,
703
+ ...message.offset !== void 0 && { offset: message.offset }
404
704
  });
405
705
  else if (message.type === "stdin_ack") {} else resolveOutcome({
406
706
  kind: "failed",
@@ -510,6 +810,59 @@ function buildOpenMessage(start) {
510
810
  };
511
811
  }
512
812
  //#endregion
813
+ //#region src/exec/invalid-resume-error.ts
814
+ var InvalidResumeError = class extends ExecError {
815
+ constructor(message, data) {
816
+ super("INVALID_RESUME", message, { data });
817
+ this.name = "InvalidResumeError";
818
+ }
819
+ };
820
+ //#endregion
821
+ //#region src/exec/invalid-state-error.ts
822
+ var InvalidStateError = class extends ExecError {
823
+ constructor(message, data) {
824
+ super("INVALID_STATE", message, { data });
825
+ this.name = "InvalidStateError";
826
+ }
827
+ };
828
+ //#endregion
829
+ //#region src/exec/no-session-error.ts
830
+ var NoSessionError = class extends ExecError {
831
+ constructor(message, data) {
832
+ super("NO_SESSION", message, { data });
833
+ this.name = "NoSessionError";
834
+ }
835
+ };
836
+ //#endregion
837
+ //#region src/exec/to-exec-error.ts
838
+ function toExecError(outcome) {
839
+ if (outcome.kind === "failed") return toFailedError(outcome.code, outcome.message, outcome.data);
840
+ if (outcome.kind === "unauthorized") return new ExecError("UNAUTHORIZED", outcome.ticketRefused === true ? "impd refused the exec ticket: it expired (30 s) or was used already" : "impd rejected the token");
841
+ if (outcome.kind === "unreachable") return new ExecError("UNREACHABLE", `cannot reach impd (${outcome.detail})`);
842
+ if (outcome.kind === "detached") return new ExecError("DETACHED", `detached from the session (${outcome.reason})`, { data: {
843
+ reason: outcome.reason,
844
+ ...outcome.offset !== void 0 && { offset: outcome.offset }
845
+ } });
846
+ if (outcome.kind === "closed") return outcome.closeCode === 1012 ? new ExecError("RESTARTING", "impd is restarting") : new ExecError("CONNECTION_CLOSED", `exec connection closed (${outcome.reason})`);
847
+ if (outcome.kind === "bad_message") return new ExecError("BAD_MESSAGE", `bad message from impd: ${outcome.detail}`);
848
+ return new ExecError("LOCAL_ERROR", "an output handler threw", { cause: outcome.error });
849
+ }
850
+ function toFailedError(code, message, data) {
851
+ if (code === "NO_SESSION") {
852
+ const parsed = NoSessionDataSchema.safeParse(data);
853
+ return new NoSessionError(message, parsed.success ? parsed.data : void 0);
854
+ }
855
+ if (code === "INVALID_STATE") {
856
+ const parsed = IMP_ERRORS.INVALID_STATE.data.safeParse(data);
857
+ if (parsed.success) return new InvalidStateError(message, parsed.data);
858
+ }
859
+ if (code === "INVALID_RESUME") {
860
+ const parsed = InvalidResumeDataSchema.safeParse(data);
861
+ if (parsed.success) return new InvalidResumeError(message, parsed.data);
862
+ }
863
+ return new ExecError(code ?? "EXEC_ERROR", message, { data });
864
+ }
865
+ //#endregion
513
866
  //#region src/exec/open-exec.ts
514
867
  const DEFAULT_MAX_UNREAD_BYTES = 8388608;
515
868
  const TTY_SIGNAL_KEYS = {
@@ -527,7 +880,8 @@ function openExec(deps, name, argv, options = {}) {
527
880
  ...options.cols !== void 0 && { cols: options.cols },
528
881
  ...options.rows !== void 0 && { rows: options.rows },
529
882
  ...options.session !== void 0 && { session: options.session },
530
- ...options.killGraceMs !== void 0 && { killGraceMs: options.killGraceMs }
883
+ ...options.killGraceMs !== void 0 && { killGraceMs: options.killGraceMs },
884
+ ...options.resumeFrom !== void 0 && { resumeFrom: options.resumeFrom }
531
885
  });
532
886
  }
533
887
  function openAttach(deps, name, session, options = {}) {
@@ -535,7 +889,9 @@ function openAttach(deps, name, session, options = {}) {
535
889
  name,
536
890
  session,
537
891
  ...options.cols !== void 0 && { cols: options.cols },
538
- ...options.rows !== void 0 && { rows: options.rows }
892
+ ...options.rows !== void 0 && { rows: options.rows },
893
+ ...options.resumeFrom !== void 0 && { resumeFrom: options.resumeFrom },
894
+ ...options.wake !== void 0 && { wake: options.wake }
539
895
  });
540
896
  }
541
897
  async function openHandle(deps, options, tty, start) {
@@ -606,7 +962,8 @@ async function openHandle(deps, options, tty, start) {
606
962
  const outcome = await session.outcome;
607
963
  const result = outcome.kind === "exit" ? { value: {
608
964
  code: outcome.code,
609
- signal: outcome.signal
965
+ signal: outcome.signal,
966
+ ...outcome.offset !== void 0 && { offset: outcome.offset }
610
967
  } } : { error: toExecError(outcome) };
611
968
  resolveHandle(result);
612
969
  };
@@ -1046,4 +1403,4 @@ function openReverseForward(options) {
1046
1403
  };
1047
1404
  }
1048
1405
  //#endregion
1049
- export { CLIENT_VERSION, CONSOLE_SHELL, ExecError, ORPCError, createImpClient, isDefinedError, openExecSession, openReverseForward, safe };
1406
+ export { CLIENT_VERSION, CONSOLE_SHELL, ExecError, InvalidResumeError, InvalidStateError, NoSessionError, ORPCError, createImpClient, isDefinedError, openExecSession, openReverseForward, safe };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zgeoff/imp-client",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Typed client for impd, the imp host daemon: imps, checkpoints, images and exec",
5
5
  "license": "MIT",
6
6
  "repository": {