ghostdock 0.0.15 → 0.0.16

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.
@@ -1106,6 +1106,8 @@ ghostdock sandboxes files upload <sandbox-id> [path] [flags]
1106
1106
  | --- | --- |
1107
1107
  | `<sandbox-id>` | Sandbox name or id. Required. |
1108
1108
  | `--org <value>` | Organization id or slug, from `ghostdock organizations list` (default: GHOSTDOCK_ORG, then `ghostdock organizations use`). |
1109
+ | `--container <value>` | Container the files land in, by name, from `ghostdock sandboxes containers list`. Default: the sandbox's default container, else the one running container. |
1110
+ | `--start` | Start the container first when it is not running, without asking. |
1109
1111
  | `--zip <value>` | Local ZIP file to upload. |
1110
1112
  | `<path>` | File or directory to upload (additional paths are allowed). |
1111
1113
 
package/dist/cli.md CHANGED
@@ -451,11 +451,16 @@ multiple files, or a large ZIP, use the dedicated multipart upload command inste
451
451
  ghostdock sandboxes files upload quiet-harbor ./dist
452
452
  ghostdock sandboxes files upload quiet-harbor ./index.html ./assets
453
453
  ghostdock sandboxes files upload quiet-harbor --zip ./site.zip
454
+ ghostdock sandboxes files upload quiet-harbor ./dist --container web --start
454
455
  ```
455
456
 
456
457
  Each positional path may be a file or directory. Directory contents are uploaded recursively,
457
458
  preserving their relative paths; a file is uploaded by its basename. A `sandboxes files upload` call needs
458
459
  at least one path or `--zip`. Uploading to a stopped sandbox resumes it automatically.
460
+ The files land in a container as the other `files` commands do (see
461
+ [Containers in a sandbox](#containers-in-a-sandbox)): `--container <name>`, else the default,
462
+ else the one running, and the CLI asks which one when it cannot tell. A stopped target container
463
+ is started after a "Start it?" at the keyboard, or at once with `--start`.
459
464
 
460
465
  ### `ghostdock env`
461
466
 
package/dist/index.js CHANGED
@@ -9176,7 +9176,7 @@ function getCliName(argv = process.argv) {
9176
9176
  return CLI_NAMES.find((name) => name === invoked) ?? "ghostdock";
9177
9177
  }
9178
9178
  // package.json
9179
- var version = "0.0.15";
9179
+ var version = "0.0.16";
9180
9180
  var package_default = {
9181
9181
  name: "ghostdock",
9182
9182
  description: "Ghostdock CLI — device login and agent tool invocation from the command line",
@@ -9781,77 +9781,6 @@ var envCommand = defineCommand({
9781
9781
  }
9782
9782
  });
9783
9783
 
9784
- // src/container-logs.ts
9785
- var FOLLOW_INTERVAL_MS = 2000;
9786
- function defaultLogsDeps(follow) {
9787
- let stop = false;
9788
- if (follow) {
9789
- process.once("SIGINT", () => {
9790
- stop = true;
9791
- });
9792
- }
9793
- return {
9794
- request: (input) => authorizedRequest("/api/v1/tools/sandboxes_containers_logs", {
9795
- method: "POST",
9796
- body: JSON.stringify(input)
9797
- }),
9798
- out: (line) => process.stdout.write(`${line}
9799
- `),
9800
- err: (line) => process.stderr.write(`${line}
9801
- `),
9802
- sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
9803
- stopped: () => stop
9804
- };
9805
- }
9806
- async function runContainerLogs(input, options, deps = defaultLogsDeps(options.follow)) {
9807
- const json = isJsonOutput();
9808
- const color = colorEnabled(process.stdout);
9809
- const print = (line) => {
9810
- if (json) {
9811
- deps.out(JSON.stringify(line));
9812
- return;
9813
- }
9814
- const text = options.timestamps && line.time ? `${dim(line.time, color)} ${line.text}` : line.text;
9815
- (line.stream === "stderr" ? deps.err : deps.out)(text);
9816
- };
9817
- let result = await deps.request(input);
9818
- if (!result.running) {
9819
- deps.err("The sandbox is stopped: it has no logs to show.");
9820
- return 1;
9821
- }
9822
- let last = null;
9823
- for (;; ) {
9824
- for (const line of result.lines) {
9825
- print(line);
9826
- if (line.time)
9827
- last = line.time;
9828
- }
9829
- if (!options.follow) {
9830
- if (result.truncated && json) {
9831
- deps.out(JSON.stringify({ truncated: true }));
9832
- } else if (result.truncated) {
9833
- deps.err(dim("(older lines left out: use --tail for more)", colorEnabled(process.stderr)));
9834
- }
9835
- return 0;
9836
- }
9837
- if (!result.truncated)
9838
- await deps.sleep(FOLLOW_INTERVAL_MS);
9839
- if (deps.stopped())
9840
- return 0;
9841
- const { tail: _tail, ...rest } = input;
9842
- try {
9843
- result = await deps.request(last ? { ...rest, after: last } : input);
9844
- } catch (error) {
9845
- deps.err(red(error instanceof Error ? error.message : String(error), colorEnabled(process.stderr)));
9846
- return 1;
9847
- }
9848
- if (!result.running) {
9849
- deps.err("The sandbox stopped.");
9850
- return 0;
9851
- }
9852
- }
9853
- }
9854
-
9855
9784
  // src/container-picker.ts
9856
9785
  import { createInterface } from "node:readline/promises";
9857
9786
  function notRunningContainerOf(error) {
@@ -9985,6 +9914,130 @@ async function withProgress(message, work, options) {
9985
9914
  }
9986
9915
  }
9987
9916
 
9917
+ // src/container-flow.ts
9918
+ async function withContainerPick(call, send, pickers, notice) {
9919
+ try {
9920
+ return await send();
9921
+ } catch (err) {
9922
+ const question = containerQuestionOf(err);
9923
+ if (!pickers || !question || !call.canPick)
9924
+ throw err;
9925
+ const container = await pickers.choose(question);
9926
+ if (!container)
9927
+ throw err;
9928
+ call.setContainer(container);
9929
+ const result = await send();
9930
+ if (call.sandboxId !== undefined && await pickers.confirmDefault(container)) {
9931
+ try {
9932
+ await authorizedRequest("/api/v1/tools/sandboxes_default_container", {
9933
+ method: "POST",
9934
+ body: JSON.stringify({
9935
+ organization: call.organization,
9936
+ sandboxId: call.sandboxId,
9937
+ container
9938
+ })
9939
+ });
9940
+ notice(eventLine("done", `Default container: ${container}.`));
9941
+ } catch (err) {
9942
+ notice(eventLine("failed", `The default container was not set: ${err instanceof Error ? err.message : String(err)}`));
9943
+ }
9944
+ }
9945
+ return result;
9946
+ }
9947
+ }
9948
+ async function withContainerStart(call, send, start, pickers, notice) {
9949
+ try {
9950
+ return await send();
9951
+ } catch (err) {
9952
+ const container = notRunningContainerOf(err);
9953
+ if (!container || call.sandboxId === undefined)
9954
+ throw err;
9955
+ if (!start && !(pickers && await pickers.confirmStart(container)))
9956
+ throw err;
9957
+ await withProgress("Starting container…", () => authorizedRequest("/api/v1/tools/sandboxes_containers_start", {
9958
+ method: "POST",
9959
+ body: JSON.stringify({
9960
+ organization: call.organization,
9961
+ sandboxId: call.sandboxId,
9962
+ container
9963
+ })
9964
+ }));
9965
+ notice(eventLine("done", `Started ${container}.`));
9966
+ return await send();
9967
+ }
9968
+ }
9969
+
9970
+ // src/container-logs.ts
9971
+ var FOLLOW_INTERVAL_MS = 2000;
9972
+ function defaultLogsDeps(follow) {
9973
+ let stop = false;
9974
+ if (follow) {
9975
+ process.once("SIGINT", () => {
9976
+ stop = true;
9977
+ });
9978
+ }
9979
+ return {
9980
+ request: (input) => authorizedRequest("/api/v1/tools/sandboxes_containers_logs", {
9981
+ method: "POST",
9982
+ body: JSON.stringify(input)
9983
+ }),
9984
+ out: (line) => process.stdout.write(`${line}
9985
+ `),
9986
+ err: (line) => process.stderr.write(`${line}
9987
+ `),
9988
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
9989
+ stopped: () => stop
9990
+ };
9991
+ }
9992
+ async function runContainerLogs(input, options, deps = defaultLogsDeps(options.follow)) {
9993
+ const json = isJsonOutput();
9994
+ const color = colorEnabled(process.stdout);
9995
+ const print = (line) => {
9996
+ if (json) {
9997
+ deps.out(JSON.stringify(line));
9998
+ return;
9999
+ }
10000
+ const text = options.timestamps && line.time ? `${dim(line.time, color)} ${line.text}` : line.text;
10001
+ (line.stream === "stderr" ? deps.err : deps.out)(text);
10002
+ };
10003
+ let result = await deps.request(input);
10004
+ if (!result.running) {
10005
+ deps.err("The sandbox is stopped: it has no logs to show.");
10006
+ return 1;
10007
+ }
10008
+ let last = null;
10009
+ for (;; ) {
10010
+ for (const line of result.lines) {
10011
+ print(line);
10012
+ if (line.time)
10013
+ last = line.time;
10014
+ }
10015
+ if (!options.follow) {
10016
+ if (result.truncated && json) {
10017
+ deps.out(JSON.stringify({ truncated: true }));
10018
+ } else if (result.truncated) {
10019
+ deps.err(dim("(older lines left out: use --tail for more)", colorEnabled(process.stderr)));
10020
+ }
10021
+ return 0;
10022
+ }
10023
+ if (!result.truncated)
10024
+ await deps.sleep(FOLLOW_INTERVAL_MS);
10025
+ if (deps.stopped())
10026
+ return 0;
10027
+ const { tail: _tail, ...rest } = input;
10028
+ try {
10029
+ result = await deps.request(last ? { ...rest, after: last } : input);
10030
+ } catch (error) {
10031
+ deps.err(red(error instanceof Error ? error.message : String(error), colorEnabled(process.stderr)));
10032
+ return 1;
10033
+ }
10034
+ if (!result.running) {
10035
+ deps.err("The sandbox stopped.");
10036
+ return 0;
10037
+ }
10038
+ }
10039
+ }
10040
+
9988
10041
  // src/human.ts
9989
10042
  var ID_KEYS = [
9990
10043
  "sandboxId",
@@ -10288,10 +10341,10 @@ function buildToolInput(schema, rawArgs) {
10288
10341
 
10289
10342
  // src/commands/invoke.ts
10290
10343
  async function runToolInvoke(tool, rawArgs, log = console.log, error = printError, notice = printNotice, pickers = defaultContainerPickers()) {
10344
+ const properties = tool.inputSchema.properties;
10291
10345
  let input;
10292
10346
  try {
10293
10347
  input = buildToolInput(tool.inputSchema, rawArgs);
10294
- const properties = tool.inputSchema.properties;
10295
10348
  if (properties && "organization" in properties && input.organization === undefined) {
10296
10349
  input.organization = await requireOrganizationRef(undefined, notice);
10297
10350
  }
@@ -10310,24 +10363,33 @@ async function runToolInvoke(tool, rawArgs, log = console.log, error = printErro
10310
10363
  return 1;
10311
10364
  }
10312
10365
  }
10366
+ const call = {
10367
+ organization: typeof input.organization === "string" ? input.organization : undefined,
10368
+ sandboxId: typeof input.sandboxId === "string" ? input.sandboxId : undefined,
10369
+ canPick: !!properties && "container" in properties && !input.container,
10370
+ setContainer: (container) => {
10371
+ input.container = container;
10372
+ }
10373
+ };
10313
10374
  try {
10314
- const send = () => authorizedRequest(`/api/v1/tools/${tool.name}`, {
10375
+ const post = () => authorizedRequest(`/api/v1/tools/${tool.name}`, {
10315
10376
  method: "POST",
10316
10377
  body: JSON.stringify(input)
10317
10378
  });
10318
- const request = () => withContainerStart(input, send, rawArgs.start === true, pickers, notice);
10319
10379
  const waitMessage = PROGRESS_MESSAGES[tool.name];
10320
- if (!waitMessage) {
10321
- const result = await withContainerPick(tool, input, request, pickers, notice);
10322
- printResult(result, () => renderResult(result, colorEnabled(process.stdout)), log);
10323
- stopNote(result, notice);
10324
- return 0;
10325
- }
10326
- const { value: result, elapsed } = await withContainerPick(tool, input, () => withProgress(waitMessage, request), pickers, notice);
10380
+ let elapsed;
10381
+ const send = async () => {
10382
+ if (!waitMessage)
10383
+ return post();
10384
+ const timed = await withProgress(waitMessage, post);
10385
+ elapsed = timed.elapsed;
10386
+ return timed.value;
10387
+ };
10388
+ const result = await withContainerPick(call, () => withContainerStart(call, send, rawArgs.start === true, pickers, notice), pickers, notice);
10327
10389
  printResult(result, () => {
10328
10390
  const color = colorEnabled(process.stdout);
10329
10391
  const lines = renderResult(result, color);
10330
- return bootLines(result, color).length > 0 ? lines : withElapsed(lines, elapsed);
10392
+ return elapsed === undefined || bootLines(result, color).length > 0 ? lines : withElapsed(lines, elapsed);
10331
10393
  }, log);
10332
10394
  stopNote(result, notice);
10333
10395
  return 0;
@@ -10336,59 +10398,6 @@ async function runToolInvoke(tool, rawArgs, log = console.log, error = printErro
10336
10398
  return 1;
10337
10399
  }
10338
10400
  }
10339
- async function withContainerPick(tool, input, send, pickers, notice) {
10340
- try {
10341
- return await send();
10342
- } catch (err) {
10343
- const properties = tool.inputSchema.properties;
10344
- const question = containerQuestionOf(err);
10345
- if (!pickers || !question || !properties || !("container" in properties) || input.container) {
10346
- throw err;
10347
- }
10348
- const container = await pickers.choose(question);
10349
- if (!container)
10350
- throw err;
10351
- input.container = container;
10352
- const result = await send();
10353
- if (typeof input.sandboxId === "string" && await pickers.confirmDefault(container)) {
10354
- try {
10355
- await authorizedRequest("/api/v1/tools/sandboxes_default_container", {
10356
- method: "POST",
10357
- body: JSON.stringify({
10358
- organization: input.organization,
10359
- sandboxId: input.sandboxId,
10360
- container
10361
- })
10362
- });
10363
- notice(eventLine("done", `Default container: ${container}.`));
10364
- } catch (err) {
10365
- notice(eventLine("failed", `The default container was not set: ${err instanceof Error ? err.message : String(err)}`));
10366
- }
10367
- }
10368
- return result;
10369
- }
10370
- }
10371
- async function withContainerStart(input, send, start, pickers, notice) {
10372
- try {
10373
- return await send();
10374
- } catch (err) {
10375
- const container = notRunningContainerOf(err);
10376
- if (!container || typeof input.sandboxId !== "string")
10377
- throw err;
10378
- if (!start && !(pickers && await pickers.confirmStart(container)))
10379
- throw err;
10380
- await authorizedRequest("/api/v1/tools/sandboxes_containers_start", {
10381
- method: "POST",
10382
- body: JSON.stringify({
10383
- organization: input.organization,
10384
- sandboxId: input.sandboxId,
10385
- container
10386
- })
10387
- });
10388
- notice(eventLine("done", `Started ${container}.`));
10389
- return await send();
10390
- }
10391
- }
10392
10401
  function stopNote(result, notice) {
10393
10402
  if (!result || typeof result !== "object")
10394
10403
  return;
@@ -12745,7 +12754,7 @@ async function collectPath3(path) {
12745
12754
  await visit(path);
12746
12755
  return files;
12747
12756
  }
12748
- async function runSandboxUpload(options, log = console.log, error = printError) {
12757
+ async function runSandboxUpload(options, log = console.log, error = printError, notice = printNotice, pickers = defaultContainerPickers()) {
12749
12758
  if (options.paths.length === 0 && !options.zip) {
12750
12759
  error("Provide at least one path or --zip.");
12751
12760
  return 1;
@@ -12761,9 +12770,26 @@ async function runSandboxUpload(options, log = console.log, error = printError)
12761
12770
  if (options.zip) {
12762
12771
  form.append("zip", new Blob([await readFile7(options.zip)]), basename5(options.zip));
12763
12772
  }
12764
- const { value: result } = await withProgress("Uploading…", () => authorizedRequest(`/api/v1/sandboxes/${encodeURIComponent(options.sandboxId)}/files?organization=${encodeURIComponent(organization)}`, { method: "POST", body: form }));
12773
+ let container = options.container;
12774
+ const call = {
12775
+ organization,
12776
+ sandboxId: options.sandboxId,
12777
+ canPick: !container,
12778
+ setContainer: (picked) => {
12779
+ container = picked;
12780
+ }
12781
+ };
12782
+ const send = async () => {
12783
+ const query = new URLSearchParams({ organization });
12784
+ if (container)
12785
+ query.set("container", container);
12786
+ const { value } = await withProgress("Uploading…", () => authorizedRequest(`/api/v1/sandboxes/${encodeURIComponent(options.sandboxId)}/files?${query}`, { method: "POST", body: form }));
12787
+ return value;
12788
+ };
12789
+ const result = await withContainerPick(call, () => withContainerStart(call, send, options.start === true, pickers, notice), pickers, notice);
12790
+ const count = `${result.written.length} file${result.written.length === 1 ? "" : "s"}`;
12765
12791
  printResult(result, () => [
12766
- eventLine("done", `Uploaded ${result.written.length} file${result.written.length === 1 ? "" : "s"} to ${options.sandboxId}.`)
12792
+ eventLine("done", result.container ? `Uploaded ${count} to ${result.container} in ${options.sandboxId}.` : `Uploaded ${count} to ${options.sandboxId}.`)
12767
12793
  ], log);
12768
12794
  return 0;
12769
12795
  } catch (err) {
@@ -12787,6 +12813,14 @@ var sandboxUploadCommand = defineCommand({
12787
12813
  required: true
12788
12814
  },
12789
12815
  org: orgArg,
12816
+ container: {
12817
+ type: "string",
12818
+ description: `Container the files land in, by name, from \`${getCliName()} sandboxes containers list\`. Default: the sandbox's default container, else the one running container.`
12819
+ },
12820
+ start: {
12821
+ type: "boolean",
12822
+ description: "Start the container first when it is not running, without asking."
12823
+ },
12790
12824
  zip: {
12791
12825
  type: "string",
12792
12826
  description: "Local ZIP file to upload."
@@ -12802,7 +12836,9 @@ var sandboxUploadCommand = defineCommand({
12802
12836
  sandboxId: args["sandbox-id"],
12803
12837
  org: args.org,
12804
12838
  paths: args._.slice(1),
12805
- zip: args.zip
12839
+ zip: args.zip,
12840
+ container: args.container,
12841
+ start: args.start
12806
12842
  });
12807
12843
  }
12808
12844
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ghostdock",
3
3
  "description": "Ghostdock CLI — device login and agent tool invocation from the command line",
4
- "version": "0.0.15",
4
+ "version": "0.0.16",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "ghostdock": "dist/index.js",