lim 0.45.1 → 0.46.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.
- package/JSON_OUTPUT.md +123 -0
- package/README.md +34 -5
- package/bin/dev.js +6 -2
- package/bin/json-errors.cjs +35 -0
- package/bin/run.js +3 -0
- package/dist/base-command.d.ts +8 -2
- package/dist/base-command.d.ts.map +1 -1
- package/dist/base-command.js +77 -15
- package/dist/base-command.js.map +1 -1
- package/dist/base-command.snapshots.test.d.ts +2 -0
- package/dist/base-command.snapshots.test.d.ts.map +1 -0
- package/dist/base-command.snapshots.test.js +215 -0
- package/dist/base-command.snapshots.test.js.map +1 -0
- package/dist/commands/android/app-log.d.ts.map +1 -1
- package/dist/commands/android/app-log.js +10 -1
- package/dist/commands/android/app-log.js.map +1 -1
- package/dist/commands/android/camera.d.ts.map +1 -1
- package/dist/commands/android/camera.js +8 -2
- package/dist/commands/android/camera.js.map +1 -1
- package/dist/commands/android/connect.d.ts.map +1 -1
- package/dist/commands/android/connect.js +2 -2
- package/dist/commands/android/connect.js.map +1 -1
- package/dist/commands/android/create.d.ts.map +1 -1
- package/dist/commands/android/create.js +10 -1
- package/dist/commands/android/create.js.map +1 -1
- package/dist/commands/android/delete.js +1 -1
- package/dist/commands/android/delete.js.map +1 -1
- package/dist/commands/android/install-app.d.ts.map +1 -1
- package/dist/commands/android/install-app.js +4 -2
- package/dist/commands/android/install-app.js.map +1 -1
- package/dist/commands/android/launch-app.d.ts.map +1 -1
- package/dist/commands/android/launch-app.js +20 -5
- package/dist/commands/android/launch-app.js.map +1 -1
- package/dist/commands/android/logcat.js +1 -1
- package/dist/commands/android/logcat.js.map +1 -1
- package/dist/commands/android/open-url.d.ts.map +1 -1
- package/dist/commands/android/open-url.js +1 -1
- package/dist/commands/android/open-url.js.map +1 -1
- package/dist/commands/android/press-key.d.ts.map +1 -1
- package/dist/commands/android/press-key.js +1 -1
- package/dist/commands/android/press-key.js.map +1 -1
- package/dist/commands/android/record.d.ts.map +1 -1
- package/dist/commands/android/record.js +26 -5
- package/dist/commands/android/record.js.map +1 -1
- package/dist/commands/android/scroll.d.ts.map +1 -1
- package/dist/commands/android/scroll.js +7 -1
- package/dist/commands/android/scroll.js.map +1 -1
- package/dist/commands/android/set-wifi-bandwidth.d.ts.map +1 -1
- package/dist/commands/android/set-wifi-bandwidth.js +1 -1
- package/dist/commands/android/set-wifi-bandwidth.js.map +1 -1
- package/dist/commands/android/sync.d.ts.map +1 -1
- package/dist/commands/android/sync.js +10 -2
- package/dist/commands/android/sync.js.map +1 -1
- package/dist/commands/android/tap.d.ts.map +1 -1
- package/dist/commands/android/tap.js +1 -1
- package/dist/commands/android/tap.js.map +1 -1
- package/dist/commands/android/terminate-app.d.ts.map +1 -1
- package/dist/commands/android/terminate-app.js +1 -1
- package/dist/commands/android/terminate-app.js.map +1 -1
- package/dist/commands/android/tunnel/index.js +2 -2
- package/dist/commands/android/tunnel/index.js.map +1 -1
- package/dist/commands/android/type.d.ts.map +1 -1
- package/dist/commands/android/type.js +1 -1
- package/dist/commands/android/type.js.map +1 -1
- package/dist/commands/asset/delete.js +1 -1
- package/dist/commands/asset/delete.js.map +1 -1
- package/dist/commands/asset/pull.js +1 -1
- package/dist/commands/asset/pull.js.map +1 -1
- package/dist/commands/asset/push.d.ts.map +1 -1
- package/dist/commands/asset/push.js +4 -0
- package/dist/commands/asset/push.js.map +1 -1
- package/dist/commands/bazel/credentials/get.d.ts.map +1 -1
- package/dist/commands/bazel/credentials/get.js +6 -1
- package/dist/commands/bazel/credentials/get.js.map +1 -1
- package/dist/commands/gradle/build.d.ts +1 -0
- package/dist/commands/gradle/build.d.ts.map +1 -1
- package/dist/commands/gradle/build.js +21 -2
- package/dist/commands/gradle/build.js.map +1 -1
- package/dist/commands/gradle/delete.js +1 -1
- package/dist/commands/gradle/delete.js.map +1 -1
- package/dist/commands/gradle/sync.d.ts.map +1 -1
- package/dist/commands/gradle/sync.js +10 -2
- package/dist/commands/gradle/sync.js.map +1 -1
- package/dist/commands/ios/app-log.d.ts.map +1 -1
- package/dist/commands/ios/app-log.js +6 -3
- package/dist/commands/ios/app-log.js.map +1 -1
- package/dist/commands/ios/camera.d.ts.map +1 -1
- package/dist/commands/ios/camera.js +8 -2
- package/dist/commands/ios/camera.js.map +1 -1
- package/dist/commands/ios/create.d.ts +4 -0
- package/dist/commands/ios/create.d.ts.map +1 -1
- package/dist/commands/ios/create.js +17 -1
- package/dist/commands/ios/create.js.map +1 -1
- package/dist/commands/ios/delete.js +1 -1
- package/dist/commands/ios/delete.js.map +1 -1
- package/dist/commands/ios/keychain/generate-key.d.ts.map +1 -1
- package/dist/commands/ios/keychain/generate-key.js +2 -1
- package/dist/commands/ios/keychain/generate-key.js.map +1 -1
- package/dist/commands/ios/launch-app.d.ts.map +1 -1
- package/dist/commands/ios/launch-app.js +19 -4
- package/dist/commands/ios/launch-app.js.map +1 -1
- package/dist/commands/ios/maestro.d.ts.map +1 -1
- package/dist/commands/ios/maestro.js +4 -1
- package/dist/commands/ios/maestro.js.map +1 -1
- package/dist/commands/ios/open-url.d.ts.map +1 -1
- package/dist/commands/ios/open-url.js +1 -1
- package/dist/commands/ios/open-url.js.map +1 -1
- package/dist/commands/ios/press-key.d.ts.map +1 -1
- package/dist/commands/ios/press-key.js +1 -1
- package/dist/commands/ios/press-key.js.map +1 -1
- package/dist/commands/ios/record.d.ts.map +1 -1
- package/dist/commands/ios/record.js +28 -5
- package/dist/commands/ios/record.js.map +1 -1
- package/dist/commands/ios/scroll.d.ts.map +1 -1
- package/dist/commands/ios/scroll.js +9 -1
- package/dist/commands/ios/scroll.js.map +1 -1
- package/dist/commands/ios/swipe.d.ts.map +1 -1
- package/dist/commands/ios/swipe.js +1 -1
- package/dist/commands/ios/swipe.js.map +1 -1
- package/dist/commands/ios/sync.d.ts.map +1 -1
- package/dist/commands/ios/sync.js +12 -3
- package/dist/commands/ios/sync.js.map +1 -1
- package/dist/commands/ios/syslog.js +1 -1
- package/dist/commands/ios/syslog.js.map +1 -1
- package/dist/commands/ios/tap.d.ts.map +1 -1
- package/dist/commands/ios/tap.js +1 -1
- package/dist/commands/ios/tap.js.map +1 -1
- package/dist/commands/ios/terminate-app.d.ts.map +1 -1
- package/dist/commands/ios/terminate-app.js +1 -1
- package/dist/commands/ios/terminate-app.js.map +1 -1
- package/dist/commands/ios/toggle-keyboard.js +1 -1
- package/dist/commands/ios/toggle-keyboard.js.map +1 -1
- package/dist/commands/ios/tunnel/index.js +2 -2
- package/dist/commands/ios/tunnel/index.js.map +1 -1
- package/dist/commands/ios/type.d.ts.map +1 -1
- package/dist/commands/ios/type.js +7 -1
- package/dist/commands/ios/type.js.map +1 -1
- package/dist/commands/logout.js +1 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/run.d.ts +4 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +15 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/session/start.d.ts.map +1 -1
- package/dist/commands/session/start.js +2 -2
- package/dist/commands/session/start.js.map +1 -1
- package/dist/commands/session/status.js +2 -2
- package/dist/commands/session/status.js.map +1 -1
- package/dist/commands/session/stop.d.ts.map +1 -1
- package/dist/commands/session/stop.js +12 -5
- package/dist/commands/session/stop.js.map +1 -1
- package/dist/commands/tunnel/delete.d.ts.map +1 -1
- package/dist/commands/tunnel/delete.js +1 -1
- package/dist/commands/tunnel/delete.js.map +1 -1
- package/dist/commands/tunnel/run.d.ts.map +1 -1
- package/dist/commands/tunnel/run.js +6 -3
- package/dist/commands/tunnel/run.js.map +1 -1
- package/dist/commands/xcode/build.d.ts +1 -0
- package/dist/commands/xcode/build.d.ts.map +1 -1
- package/dist/commands/xcode/build.js +25 -7
- package/dist/commands/xcode/build.js.map +1 -1
- package/dist/commands/xcode/create.d.ts +1 -0
- package/dist/commands/xcode/create.d.ts.map +1 -1
- package/dist/commands/xcode/create.js +13 -4
- package/dist/commands/xcode/create.js.map +1 -1
- package/dist/commands/xcode/delete.js +1 -1
- package/dist/commands/xcode/delete.js.map +1 -1
- package/dist/commands/xcode/rbe/index.d.ts.map +1 -1
- package/dist/commands/xcode/rbe/index.js +6 -0
- package/dist/commands/xcode/rbe/index.js.map +1 -1
- package/dist/commands/xcode/sync.d.ts +4 -0
- package/dist/commands/xcode/sync.d.ts.map +1 -1
- package/dist/commands/xcode/sync.js +15 -2
- package/dist/commands/xcode/sync.js.map +1 -1
- package/dist/commands/xcode/test.d.ts +1 -0
- package/dist/commands/xcode/test.d.ts.map +1 -1
- package/dist/commands/xcode/test.js +1 -0
- package/dist/commands/xcode/test.js.map +1 -1
- package/dist/json-contract.test.d.ts +2 -0
- package/dist/json-contract.test.d.ts.map +1 -0
- package/dist/json-contract.test.js +703 -0
- package/dist/json-contract.test.js.map +1 -0
- package/dist/json-errors.test.d.ts +2 -0
- package/dist/json-errors.test.d.ts.map +1 -0
- package/dist/json-errors.test.js +95 -0
- package/dist/json-errors.test.js.map +1 -0
- package/dist/lib/build-command-helpers.d.ts +4 -1
- package/dist/lib/build-command-helpers.d.ts.map +1 -1
- package/dist/lib/build-command-helpers.js +22 -3
- package/dist/lib/build-command-helpers.js.map +1 -1
- package/dist/lib/build-run-command.d.ts.map +1 -1
- package/dist/lib/build-run-command.js +1 -1
- package/dist/lib/build-run-command.js.map +1 -1
- package/dist/lib/build-tools-command.d.ts.map +1 -1
- package/dist/lib/build-tools-command.js +1 -1
- package/dist/lib/build-tools-command.js.map +1 -1
- package/dist/lib/build-use-command.d.ts.map +1 -1
- package/dist/lib/build-use-command.js +9 -2
- package/dist/lib/build-use-command.js.map +1 -1
- package/dist/lib/build-use-command.test.js +11 -0
- package/dist/lib/build-use-command.test.js.map +1 -1
- package/dist/lib/cache-flags.d.ts +1 -0
- package/dist/lib/cache-flags.d.ts.map +1 -1
- package/dist/lib/cache-flags.js +5 -1
- package/dist/lib/cache-flags.js.map +1 -1
- package/dist/lib/cache-flags.test.js +8 -0
- package/dist/lib/cache-flags.test.js.map +1 -1
- package/dist/lib/cache.d.ts +1 -0
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +3 -0
- package/dist/lib/cache.js.map +1 -1
- package/dist/lib/daemon-client.d.ts +1 -1
- package/dist/lib/daemon-client.d.ts.map +1 -1
- package/dist/lib/daemon-client.js +2 -2
- package/dist/lib/daemon-client.js.map +1 -1
- package/dist/lib/instance-client-factory.d.ts.map +1 -1
- package/dist/lib/instance-client-factory.js +13 -3
- package/dist/lib/instance-client-factory.js.map +1 -1
- package/dist/lib/instance-client-factory.test.js +35 -0
- package/dist/lib/instance-client-factory.test.js.map +1 -1
- package/dist/lib/snapshot-project.d.ts +7 -0
- package/dist/lib/snapshot-project.d.ts.map +1 -0
- package/dist/lib/snapshot-project.js +194 -0
- package/dist/lib/snapshot-project.js.map +1 -0
- package/dist/lib/snapshot-project.test.d.ts +2 -0
- package/dist/lib/snapshot-project.test.d.ts.map +1 -0
- package/dist/lib/snapshot-project.test.js +156 -0
- package/dist/lib/snapshot-project.test.js.map +1 -0
- package/package.json +4 -3
package/JSON_OUTPUT.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# CLI JSON contract
|
|
2
|
+
|
|
3
|
+
Use `--json` for automation on commands whose help lists that flag. JSON output is a public CLI interface. Preserve existing field names, types, and meanings when changing a command. Additive fields are allowed; consumers should ignore fields they do not use. Removing or renaming a field requires a documented compatibility change.
|
|
4
|
+
|
|
5
|
+
## Transport and exit status
|
|
6
|
+
|
|
7
|
+
- Non-streaming commands write one JSON value to stdout. List commands return arrays, including `[]` for no results. Resource commands retain their API/SDK response fields, rather than rendering table cells as strings.
|
|
8
|
+
- Log streams, foreground builds and app launches, `sync --watch`, `tunnel run`, and `xcode test` write newline-delimited JSON. Each line is a complete JSON value. Newlines inside strings are escaped.
|
|
9
|
+
- Commands that hold a connection, such as `android connect`, device tunnels, reverse tunnels, and RBE, write their readiness result and stay running. Creation with `--rm` also stays running after its instance response. JSON changes output, not lifecycle.
|
|
10
|
+
- Human messages and SDK diagnostics never go to stdout in JSON mode. Diagnostics may appear on stderr. `--quiet --json` has the same JSON result as `--json`.
|
|
11
|
+
- Check the process exit status. Nonzero means failure. Execution commands can return a structured result with a nonzero `exitCode` or `code`; receiving JSON alone does not mean success. A failed mutation does not emit a success acknowledgement.
|
|
12
|
+
- CLI, validation, and API errors emit `{"error":{"code":"CLI_ERROR","message":"..."}}` on stderr. A specific error code replaces `CLI_ERROR` when available; HTTP errors also include numeric `status`. stderr may also contain diagnostics, so do not parse the whole stream as one JSON document. Explicit process exits after an execution result preserve the exit status without adding another result.
|
|
13
|
+
- Pass the CLI's `--json` before `--`. Flags after `--` belong to the invoked tool. Interactive commands that do not advertise JSON, including `login` and the onboarding `run`, reject it.
|
|
14
|
+
|
|
15
|
+
## Instance and asset resources
|
|
16
|
+
|
|
17
|
+
| Commands | JSON result |
|
|
18
|
+
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `android create`, `ios create`, `xcode create`, `gradle create` | The created instance resource, including `metadata`, `spec`, and `status`. Existing attachment fields on iOS/Xcode creation are retained. |
|
|
20
|
+
| `android get`, `ios get`, `gradle get` | The instance resource. With no explicit or remembered instance, the existing fallback returns the corresponding list. |
|
|
21
|
+
| `xcode get` | The existing resolved Xcode target fields plus `simulator` status. |
|
|
22
|
+
| Instance `list` commands | Array of instance resources. |
|
|
23
|
+
| Instance `delete`, `asset delete`, `tunnel delete` | `{"ok":true,"id":"..."}`. The ID identifies the resource deleted. Instance deletion remains idempotent for an already-missing instance. |
|
|
24
|
+
| `asset push` | The uploaded asset resource, including `id`, `name`, and any API-provided expiry and URLs. |
|
|
25
|
+
| `asset list` | Array of asset resources. |
|
|
26
|
+
| `asset pull` | `{"localPath":"/absolute/path","bytes":123}`. |
|
|
27
|
+
|
|
28
|
+
`assets` aliases use the same contract as `asset`.
|
|
29
|
+
|
|
30
|
+
Android creation returns the same instance resource with or without `--connect`. The default still starts a background ADB tunnel. `--no-connect` skips it; JSON mode already suppresses browser opening. Foreground connection modes return the resource after starting the tunnel, before waiting for shutdown.
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
lim android create --hard-timeout=10m --json > instance.json
|
|
34
|
+
jq -r '.metadata.id' instance.json
|
|
35
|
+
|
|
36
|
+
# Create without starting a local ADB tunnel.
|
|
37
|
+
lim android create --hard-timeout=10m --no-connect --json > instance.json
|
|
38
|
+
lim android connect --id "$(jq -r '.metadata.id' instance.json)"
|
|
39
|
+
|
|
40
|
+
lim asset push ./app.apk --json | jq -r .id
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Instance responses include access credentials. Store them with the same care as API responses.
|
|
44
|
+
|
|
45
|
+
## Device actions and files
|
|
46
|
+
|
|
47
|
+
Existing JSON-enabled device commands keep their existing response shapes. In particular, element trees, selectors, device info, app lists, microphone operations, proxy operations, and file operations retain their API/SDK fields.
|
|
48
|
+
|
|
49
|
+
| Commands | JSON result |
|
|
50
|
+
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| iOS/Android `tap` | `{ok:true, instanceId, x, y}`; iOS adds `display` when explicitly selected. |
|
|
52
|
+
| iOS/Android `type` | `{ok:true, instanceId, textLength}`; iOS adds `enter` and `requireFocus`, Android adds `target` when selected. `textLength` counts UTF-16 code units; input text is not echoed. |
|
|
53
|
+
| iOS/Android `press-key` | `{ok:true, instanceId, key, modifiers}`. `modifiers` is an array, empty when none were requested. |
|
|
54
|
+
| iOS/Android `scroll` | `{ok:true, instanceId, direction, amount}`; iOS adds requested `momentum`, `coordinate: [x,y]`, and `display`. Android adds `target` when selected. |
|
|
55
|
+
| iOS/Android `open-url` | `{ok:true, instanceId, url}`. |
|
|
56
|
+
| iOS/Android `terminate-app` | `{ok:true, instanceId, bundleId}` on iOS; `packageName` on Android. |
|
|
57
|
+
| Detached iOS/Android `launch-app` | `{ok:true, instanceId, bundleId, mode}` on iOS; `packageName` on Android. `mode` is the effective launch mode, including defaults. iOS adds `runtime` when requested. Environment values are not echoed. |
|
|
58
|
+
| iOS `swipe` | `{ok:true, instanceId, from:[x,y], to:[x,y], durationMs}`; adds `display` when explicitly selected. |
|
|
59
|
+
| iOS `toggle-keyboard` | `{ok:true, instanceId}`. The API does not report the resulting keyboard visibility. |
|
|
60
|
+
| Android `install-app` | `{ok:true, instanceId, source}`. `source` is the supplied path or URL; local uploads also include `assetId`. Acknowledges sending the asset to the instance. |
|
|
61
|
+
| `ios install-app` | Existing installation result, including the bundle ID when supplied by the SDK. |
|
|
62
|
+
| `android set-wifi-bandwidth` | `{ok:true, instanceId, downKbps:0, upKbps:1000}`. Only requested directions are included; zero clears that limit. |
|
|
63
|
+
| iOS/Android `record start` | `{ok:true, instanceId, recording:true, quality, persist}`. Explicit retention adds `persistTtlSeconds`. iOS adds `touchIndicators` and, when requested, `display`. |
|
|
64
|
+
| iOS/Android `record stop` | `{ok:true, instanceId, recording:false, localPath, uploaded}`. `uploaded` is true after a requested presigned upload completes. |
|
|
65
|
+
| `ios keychain generate-key` | `{"key":"base64-encoded-key"}`. |
|
|
66
|
+
| `android connect` | `{"instanceId":"...","address":{"address":"127.0.0.1","port":12345}}` from the local tunnel. The command stays running. |
|
|
67
|
+
|
|
68
|
+
Camera `play` on both platforms returns `{ok:true, instanceId, action:"play", localPath, loop}`. Camera `clear` returns `{ok:true, instanceId, action:"clear"}`. Existing `ok` and `loop` fields are retained.
|
|
69
|
+
|
|
70
|
+
These action results identify the resolved instance, including when the CLI selected a remembered instance. Parameters describe the submitted action after it succeeds, not a separate observation of device state. Optional targeting fields are omitted when no override was supplied. Android `target` uses the SDK shape: `{x,y}` or `{selector:{resourceId, text, ...}}`.
|
|
71
|
+
|
|
72
|
+
Direct WebSocket connections and background session connections have the same command output contract.
|
|
73
|
+
|
|
74
|
+
## Builds, execution, sync, and logs
|
|
75
|
+
|
|
76
|
+
| Commands | JSON result |
|
|
77
|
+
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
78
|
+
| Foreground `xcode build`, `gradle build` | NDJSON `{type:"stdout",line}` and `{type:"stderr",line}` events, followed by `{type:"result",instanceId,...SDK execution result}`. The result retains `execId`, `status`, `exitCode`, artifact/store fields, `timedOut`, and `incomplete`. |
|
|
79
|
+
| `xcode build --detach`, `gradle build --detach` | Existing `instanceId`, `execId`, `consoleUrl`, `logsCommand`, and optional `webhookUrl`. Acceptance does not mean completion. |
|
|
80
|
+
| Xcode/Gradle `run`, `tools`, `tools install`, mise `use` | SDK execution result plus captured `stdout` and `stderr` strings. Mixed `xcode use xcode@... node@...` adds the Xcode selection under `xcode`. Xcode-only selection keeps its existing selection response. |
|
|
81
|
+
| iOS `simctl`, `xcrun`, `xcodebuild`, `defaults`, `notify`; Android `adb-shell` | Existing tool execution response, with stdout, stderr, and the tool's `code` or `exitCode`. |
|
|
82
|
+
| `ios maestro` | `{"exitCode":0}`; Maestro output goes to stderr. |
|
|
83
|
+
| iOS/Android/Xcode/Gradle `sync` | SDK sync result, such as `bytesSent`, and optional installed app fields. No watcher function is serialized. |
|
|
84
|
+
| `sync --watch` | One SDK sync event per line, including `bytesSent`, `durationMs`, and optional installed app fields, for the initial sync and each update. |
|
|
85
|
+
| Android `app-log` without `--follow` | Shell result containing `stdout`, `stderr`, and `exitCode`. |
|
|
86
|
+
| iOS `app-log` without `--follow` | `{"stdout":"..."}`. |
|
|
87
|
+
| Android `app-log --follow` | One API app-log capture record per line. |
|
|
88
|
+
| iOS `app-log --follow`, Android `logcat`, iOS `syslog` | One `{"line":"..."}` record per line. |
|
|
89
|
+
| Foreground iOS/Android `launch-app` | The detached launch fields with `type:"launched"` instead of `ok`; iOS log events use `{type:"log",line}`. Exit events use `type:"exit"` and the SDK app-exit fields; Android includes its log tail in `logs`. |
|
|
90
|
+
| `xcode test` | Existing XCTest event records followed by the existing terminal `{exitCode, timedOut?, incomplete?}` record. |
|
|
91
|
+
| Xcode/Gradle `logs` | Existing collected execution-log response. |
|
|
92
|
+
|
|
93
|
+
Foreground build output events preserve the order received from the SDK and identify the original stream. Lines are not buffered until the build ends. Empty lines remain empty strings, and embedded newlines are escaped. Once execution returns, exactly one `result` event ends the stream, including for failed and timed-out executions. A CLI/API failure before an execution result is available emits an error on stderr instead. `--detach` retains its single acknowledgement object.
|
|
94
|
+
|
|
95
|
+
A `timedOut` execution result means the client stopped waiting. The remote execution may still be running. Inspect `incomplete` and follow its logs instead of assuming that retrying is harmless.
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
lim gradle build . --json > build.ndjson 2> diagnostics.log
|
|
99
|
+
jq 'select(.type == "result")' build.ndjson
|
|
100
|
+
jq -r 'select(.type == "stdout" or .type == "stderr") | .line' build.ndjson
|
|
101
|
+
lim ios syslog --json | jq --unbuffered -r .line
|
|
102
|
+
lim xcode sync . --watch --json | jq --unbuffered .bytesSent
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Sessions and tunnels
|
|
106
|
+
|
|
107
|
+
| Commands | JSON result |
|
|
108
|
+
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| `session start` | `{"instanceId":"...","running":true,"alreadyRunning":false}`. |
|
|
110
|
+
| `session stop` | `{"stopped":["instance-id"]}`. Empty if nothing was running; `--all` produces one array, not one response per session. Ambiguous selection fails. |
|
|
111
|
+
| `session status` | Existing array of session status objects. |
|
|
112
|
+
| Device tunnel start/status/stop, `ios reverse` | Existing readiness/status/stop responses. |
|
|
113
|
+
| `tunnel run` | One SDK connector event per line. |
|
|
114
|
+
| Private tunnel create/list/update/rotate | Existing API tunnel responses and token fields. |
|
|
115
|
+
| `xcode rbe` | Existing readiness result. An already-running tunnel returns its stored process fields plus `running: true`. |
|
|
116
|
+
| `xcode rbe --stop` | `{"stopped":false}` if none is running; otherwise `{stopped:true, forced, instanceId, pid}`. |
|
|
117
|
+
| `xcode rbe upload`, version selection, set-instance, and `skills install` | Existing structured results are unchanged. |
|
|
118
|
+
|
|
119
|
+
## Maintaining the contract
|
|
120
|
+
|
|
121
|
+
Changes must keep JSON stdout free of human text on success, failure, empty results, daemon and direct connections, and foreground and detached paths. Use `outputJson` for finite results, `outputEvent` for streams, and `outputResult` for data with a human-readable alternative. Do not use plain logging or inherited child stdout for JSON results. Configure SDK clients so their informational logging cannot reach stdout.
|
|
122
|
+
|
|
123
|
+
The CLI tests exercise response shapes, stream framing, both device transports, default Android creation, foreground and detached behavior, failures, and the executable's error handling. Add a contract test when adding a new JSON branch or changing a response.
|
package/README.md
CHANGED
|
@@ -498,16 +498,31 @@ lim xcode version list # Xcode versions the sandbox can build with
|
|
|
498
498
|
|
|
499
499
|
### Disk snapshots
|
|
500
500
|
|
|
501
|
-
|
|
501
|
+
New Xcode instances automatically restore and save a disk snapshot using the
|
|
502
|
+
Git origin host/repository and project subdirectory, for example
|
|
503
|
+
`github.com/acme/monorepo/apps/ios`. SSH and HTTPS origins produce the same key;
|
|
504
|
+
branches and commits share it. The key alone controls both restore and save.
|
|
505
|
+
Discovery reads repository-local Git metadata directly, including worktree and
|
|
506
|
+
submodule pointers, so it does not require the Git executable. Global Git
|
|
507
|
+
settings and URL rewrites are not used; config includes require an explicit key.
|
|
508
|
+
|
|
509
|
+
`build`, `test`, and `sync` use their project path; `create` uses the current
|
|
510
|
+
directory. `lim run` uses its detected project, and `lim ios create --xcode`
|
|
511
|
+
uses the current directory. Existing instances keep their original snapshot settings. Without
|
|
512
|
+
a usable Git origin, pass `--snapshot-key` explicitly. Use `--no-snapshot` to
|
|
513
|
+
create an uncached instance.
|
|
514
|
+
|
|
515
|
+
From the project directory:
|
|
502
516
|
|
|
503
517
|
```bash
|
|
504
|
-
XCODE_ID=$(lim xcode create --
|
|
518
|
+
XCODE_ID=$(lim xcode create --quiet)
|
|
505
519
|
lim xcode build . --id "$XCODE_ID" --scheme MyApp
|
|
506
520
|
lim xcode delete "$XCODE_ID" --wait-snapshot
|
|
507
521
|
```
|
|
508
522
|
|
|
509
523
|
The first build starts cold. Termination publishes the workspace, and the next
|
|
510
|
-
instance restores it.
|
|
524
|
+
instance restores it. Override with `--snapshot-key` when separate branches or
|
|
525
|
+
Xcode versions need independent saved state. Use `--snapshot-restore-keys "myapp-pr51,myapp-main"` at
|
|
511
526
|
creation for ordered exact-key and literal-prefix fallbacks. Leave
|
|
512
527
|
`--snapshot-paths` unset to save the whole workspace. On existing instances,
|
|
513
528
|
restore keys and paths stay fixed; `build --snapshot-key` only binds a publication
|
|
@@ -994,7 +1009,7 @@ journey remains continuous. Set `LIM_DISABLE_TELEMETRY=1` to disable collection.
|
|
|
994
1009
|
|
|
995
1010
|
## JSON Output
|
|
996
1011
|
|
|
997
|
-
|
|
1012
|
+
Commands that advertise `--json` provide a machine-readable CLI contract. Finite commands return one JSON value; streams and watch modes use newline-delimited JSON. Human-readable diagnostics go to stderr. Always check the exit status. See [the JSON output contract](./JSON_OUTPUT.md) for response shapes, stream framing, and errors:
|
|
998
1013
|
|
|
999
1014
|
```bash
|
|
1000
1015
|
# Get instance details as JSON
|
|
@@ -1213,7 +1228,21 @@ lim gradle run --env APP_ENV=staging -- npm run generate
|
|
|
1213
1228
|
lim gradle build . --env APP_ENV=staging
|
|
1214
1229
|
```
|
|
1215
1230
|
|
|
1216
|
-
`--additional-file localPath=remotePath`
|
|
1231
|
+
`lim gradle build`, `sync`, and `run` accept `--additional-file localPath=remotePath`
|
|
1232
|
+
to add files outside the source tree. Repeat the flag for multiple files. Remote
|
|
1233
|
+
paths starting with `~/` target the sandbox home used by builds and tool commands;
|
|
1234
|
+
other paths are relative to the workspace.
|
|
1235
|
+
For private npm dependencies, copy an existing registry configuration:
|
|
1236
|
+
|
|
1237
|
+
```bash
|
|
1238
|
+
lim gradle build . \
|
|
1239
|
+
--additional-file "$HOME/.npmrc=~/.npmrc" \
|
|
1240
|
+
--additional-file "$HOME/.netrc=~/.netrc"
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
Pass the flag on each sync that needs the file; omitting it removes the previously
|
|
1244
|
+
synced file. Home files are shared by projects on the instance. If the configuration references
|
|
1245
|
+
an environment variable, pass it with `--env` as well.
|
|
1217
1246
|
|
|
1218
1247
|
`run` syncs the current directory first. Add `--no-sync` to use the existing
|
|
1219
1248
|
remote workspace. Its optional positional directory is relative to that
|
package/bin/dev.js
CHANGED
|
@@ -9,8 +9,12 @@ require('ts-node').register({ project });
|
|
|
9
9
|
const oclif = require('@oclif/core');
|
|
10
10
|
|
|
11
11
|
oclif.settings.debug = true;
|
|
12
|
+
process.env.NODE_ENV = 'development';
|
|
13
|
+
const { handleJsonError } = require('./json-errors.cjs');
|
|
12
14
|
|
|
13
15
|
oclif
|
|
14
|
-
.
|
|
16
|
+
.run(process.argv.slice(2), __dirname)
|
|
15
17
|
.then(oclif.flush)
|
|
16
|
-
.catch(
|
|
18
|
+
.catch(async (error) => {
|
|
19
|
+
if (!(await handleJsonError(error))) return oclif.Errors.handle(error);
|
|
20
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Keep parser and command errors machine-readable, including failures before flags are parsed.
|
|
2
|
+
async function handleJsonError(error, argv = process.argv.slice(2)) {
|
|
3
|
+
const delimiter = argv.indexOf('--');
|
|
4
|
+
if (!argv.slice(0, delimiter < 0 ? argv.length : delimiter).includes('--json')) return false;
|
|
5
|
+
const exitCode = error?.oclif?.exit ?? error?.exitCode ?? 1;
|
|
6
|
+
process.exitCode = exitCode;
|
|
7
|
+
// Explicit exits follow an already-emitted execution result, or a successful early exit.
|
|
8
|
+
if (error?.code !== 'EEXIT') {
|
|
9
|
+
process.stderr.write(
|
|
10
|
+
JSON.stringify({
|
|
11
|
+
error: {
|
|
12
|
+
code: typeof error?.code === 'string' ? error.code : 'CLI_ERROR',
|
|
13
|
+
message: error instanceof Error ? error.message : String(error),
|
|
14
|
+
...(typeof error?.status === 'number' ? { status: error.status } : {}),
|
|
15
|
+
},
|
|
16
|
+
}) + '\n',
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// SDK sockets can outlive a failed command. Flush both streams before forcing exit,
|
|
21
|
+
// with the same ten-second flush budget as oclif for a stalled output consumer.
|
|
22
|
+
const deadline = setTimeout(() => process.exit(exitCode), 10_000);
|
|
23
|
+
deadline.unref();
|
|
24
|
+
try {
|
|
25
|
+
await Promise.all(
|
|
26
|
+
[process.stdout, process.stderr].map((stream) => new Promise((resolve) => stream.write('', resolve))),
|
|
27
|
+
);
|
|
28
|
+
} finally {
|
|
29
|
+
clearTimeout(deadline);
|
|
30
|
+
process.exit(exitCode);
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { handleJsonError };
|
package/bin/run.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
const { handleJsonError } = require('./json-errors.cjs');
|
|
4
|
+
|
|
3
5
|
const { flush } = require('@oclif/core');
|
|
4
6
|
const { Config } = require('@oclif/core/config');
|
|
5
7
|
const { handle } = require('@oclif/core/errors');
|
|
@@ -36,6 +38,7 @@ async function inferCommandArgv(argv) {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
async function handleError(error) {
|
|
41
|
+
if (await handleJsonError(error)) return;
|
|
39
42
|
if (!isUsageError(error)) {
|
|
40
43
|
await handle(error);
|
|
41
44
|
return;
|
package/dist/base-command.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export declare abstract class BaseCommand extends Command {
|
|
|
39
39
|
private _client?;
|
|
40
40
|
protected get client(): Limrun;
|
|
41
41
|
private _parsedFlags?;
|
|
42
|
+
protected snapshotProjectPath?: string;
|
|
42
43
|
private _lastResolvedInstanceId?;
|
|
43
44
|
private _lastResolvedExpectedType?;
|
|
44
45
|
private _xcodeReplacementIntent?;
|
|
@@ -57,7 +58,11 @@ export declare abstract class BaseCommand extends Command {
|
|
|
57
58
|
/** ` in this workspace (<key>)`, or empty when resolved to the shared global slot. */
|
|
58
59
|
protected scopeSuffix(): string;
|
|
59
60
|
protected info(message?: string): void;
|
|
61
|
+
log(message?: string, ...args: any[]): void;
|
|
60
62
|
protected output(message?: string): void;
|
|
63
|
+
protected outputResult(data: unknown, message: string): void;
|
|
64
|
+
/** Streams use one complete JSON value per line, including embedded newlines. */
|
|
65
|
+
protected outputEvent(data: unknown): void;
|
|
61
66
|
/** Adapter passed to the shared tunnel command flows in lib/tunnel-command. */
|
|
62
67
|
protected tunnelCommandIO(): TunnelCommandIO;
|
|
63
68
|
protected withAuth<T>(fn: () => Promise<T>, options?: {
|
|
@@ -97,8 +102,9 @@ export declare abstract class BaseCommand extends Command {
|
|
|
97
102
|
private createAndroidInstance;
|
|
98
103
|
/** Whether `id` is a server-side instance THIS invocation auto-created. */
|
|
99
104
|
protected wasCreatedThisRun(id: string | undefined): boolean;
|
|
100
|
-
/**
|
|
105
|
+
/** Defaults apply only at creation; existing targets keep their snapshot configuration. */
|
|
101
106
|
protected cacheConfigFromFlags(): XcodeCacheConfig | undefined;
|
|
107
|
+
protected buildSnapshotTip(instanceId: string): Promise<string>;
|
|
102
108
|
/**
|
|
103
109
|
* Blocks while a freshly created instance restores its cache, printing each phase as it
|
|
104
110
|
* happens. A restore that fell back to a cold workspace just prints and returns, because the
|
|
@@ -149,7 +155,7 @@ export declare abstract class BaseCommand extends Command {
|
|
|
149
155
|
status: Limrun.XcodeStatus;
|
|
150
156
|
} | undefined>;
|
|
151
157
|
/** Save the workspace preference and switch its existing sandbox when available. */
|
|
152
|
-
protected setPreferredXcodeVersion(version: string, providedId?: string): Promise<void>;
|
|
158
|
+
protected setPreferredXcodeVersion(version: string, providedId?: string, renderJson?: (result: unknown) => void): Promise<void>;
|
|
153
159
|
/** setXcode with the daemon's own refusal message instead of the transport's wrapping. */
|
|
154
160
|
protected selectXcode(xcodeClient: XcodeClient, version: string): Promise<XcodeSelectResult>;
|
|
155
161
|
private readXcodeSelection;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-command.d.ts","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAC7C,OAAO,MAMN,MAAM,aAAa,CAAC;AACrB,OAAO,EAcL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAkBtB,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE5G,OAAO,EAA6C,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5G,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"base-command.d.ts","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"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"}
|
package/dist/base-command.js
CHANGED
|
@@ -48,6 +48,7 @@ const api_2 = require("@limrun/api");
|
|
|
48
48
|
const cache_1 = require("./lib/cache");
|
|
49
49
|
const xcode_version_1 = require("./lib/xcode-version");
|
|
50
50
|
const telemetry_1 = require("./lib/telemetry");
|
|
51
|
+
const snapshot_project_1 = require("./lib/snapshot-project");
|
|
51
52
|
const VERSION = require('../package.json').version;
|
|
52
53
|
// Full instance-id shape only: prefix_region_suffix with a long TypeID suffix.
|
|
53
54
|
// TypeIDs are lowercase, so no /i flag; this avoids matching bare prefix words
|
|
@@ -79,7 +80,12 @@ class BaseCommand extends core_1.Command {
|
|
|
79
80
|
// A management call with the placeholder gets a 403 marked
|
|
80
81
|
// "unauthenticated:", which withAuth turns into login guidance or a
|
|
81
82
|
// re-login.
|
|
82
|
-
this._client = new api_1.default({
|
|
83
|
+
this._client = new api_1.default({
|
|
84
|
+
apiKey: apiKey || 'unauthenticated',
|
|
85
|
+
baseURL,
|
|
86
|
+
userAgent: `lim/${VERSION}`,
|
|
87
|
+
...(this.shouldSuppressInfo() ? { logLevel: 'warn' } : {}),
|
|
88
|
+
});
|
|
83
89
|
}
|
|
84
90
|
return this._client;
|
|
85
91
|
}
|
|
@@ -133,8 +139,25 @@ class BaseCommand extends core_1.Command {
|
|
|
133
139
|
super.log(message);
|
|
134
140
|
}
|
|
135
141
|
}
|
|
142
|
+
// Keep stdout machine-readable even if a human message bypasses info().
|
|
143
|
+
log(message = '', ...args) {
|
|
144
|
+
if (this.isJsonEnabled())
|
|
145
|
+
this.logToStderr(message, ...args);
|
|
146
|
+
else
|
|
147
|
+
super.log(message, ...args);
|
|
148
|
+
}
|
|
136
149
|
output(message = '') {
|
|
137
|
-
|
|
150
|
+
this.log(message);
|
|
151
|
+
}
|
|
152
|
+
outputResult(data, message) {
|
|
153
|
+
if (this.isJsonEnabled())
|
|
154
|
+
this.outputJson(data);
|
|
155
|
+
else
|
|
156
|
+
this.output(message);
|
|
157
|
+
}
|
|
158
|
+
/** Streams use one complete JSON value per line, including embedded newlines. */
|
|
159
|
+
outputEvent(data) {
|
|
160
|
+
super.log(JSON.stringify(data));
|
|
138
161
|
}
|
|
139
162
|
/** Adapter passed to the shared tunnel command flows in lib/tunnel-command. */
|
|
140
163
|
tunnelCommandIO() {
|
|
@@ -225,14 +248,14 @@ class BaseCommand extends core_1.Command {
|
|
|
225
248
|
});
|
|
226
249
|
return obj;
|
|
227
250
|
});
|
|
228
|
-
this.
|
|
251
|
+
this.outputJson(objects);
|
|
229
252
|
}
|
|
230
253
|
else {
|
|
231
254
|
this.output((0, formatting_1.renderTable)(headers, rows));
|
|
232
255
|
}
|
|
233
256
|
}
|
|
234
257
|
outputJson(data) {
|
|
235
|
-
|
|
258
|
+
super.log(JSON.stringify(data, null, 2));
|
|
236
259
|
}
|
|
237
260
|
/**
|
|
238
261
|
* Runs the sibling `list` command (e.g. `ios:list` for `ios get`), forwarding
|
|
@@ -268,7 +291,11 @@ class BaseCommand extends core_1.Command {
|
|
|
268
291
|
async resolveXcodeClient(target) {
|
|
269
292
|
if (target.apiUrl && target.token) {
|
|
270
293
|
try {
|
|
271
|
-
return await this.client.xcodeInstances.createClient({
|
|
294
|
+
return await this.client.xcodeInstances.createClient({
|
|
295
|
+
apiUrl: target.apiUrl,
|
|
296
|
+
token: target.token,
|
|
297
|
+
logLevel: this.shouldSuppressInfo() ? 'warn' : 'info',
|
|
298
|
+
});
|
|
272
299
|
}
|
|
273
300
|
catch (err) {
|
|
274
301
|
if (this.isCachedXcodeClientNotFound(err)) {
|
|
@@ -279,7 +306,10 @@ class BaseCommand extends core_1.Command {
|
|
|
279
306
|
}
|
|
280
307
|
const instance = await this.client.xcodeInstances.get(target.id);
|
|
281
308
|
(0, config_1.registerCreatedInstance)(instance);
|
|
282
|
-
return this.client.xcodeInstances.createClient({
|
|
309
|
+
return this.client.xcodeInstances.createClient({
|
|
310
|
+
instance,
|
|
311
|
+
logLevel: this.shouldSuppressInfo() ? 'warn' : 'info',
|
|
312
|
+
});
|
|
283
313
|
}
|
|
284
314
|
tryResolveAndroidInstance(providedId) {
|
|
285
315
|
this._lastResolvedExpectedType = 'android';
|
|
@@ -617,17 +647,42 @@ class BaseCommand extends core_1.Command {
|
|
|
617
647
|
wasCreatedThisRun(id) {
|
|
618
648
|
return !!id && this._instancesCreatedThisRun.has(id);
|
|
619
649
|
}
|
|
620
|
-
/**
|
|
650
|
+
/** Defaults apply only at creation; existing targets keep their snapshot configuration. */
|
|
621
651
|
cacheConfigFromFlags() {
|
|
622
652
|
const flags = this.parsedFlags;
|
|
623
653
|
if (!flags)
|
|
624
654
|
return undefined;
|
|
625
655
|
const asString = (name) => typeof flags[name] === 'string' ? flags[name] : undefined;
|
|
626
|
-
|
|
656
|
+
const explicit = {
|
|
657
|
+
'no-snapshot': flags['no-snapshot'] === true,
|
|
627
658
|
'snapshot-key': asString('snapshot-key'),
|
|
628
659
|
'snapshot-restore-keys': asString('snapshot-restore-keys'),
|
|
629
660
|
'snapshot-paths': asString('snapshot-paths'),
|
|
630
|
-
}
|
|
661
|
+
};
|
|
662
|
+
const config = (0, snapshot_project_1.projectSnapshotConfig)(explicit, this.snapshotProjectPath ?? process.cwd());
|
|
663
|
+
if (!(0, cache_1.parseSnapshotConfig)(explicit) && !explicit['no-snapshot']) {
|
|
664
|
+
this.info(config?.key ?
|
|
665
|
+
`Disk snapshot key: ${config.key}`
|
|
666
|
+
: 'Could not derive a disk snapshot key from Git origin. Use --snapshot-key to enable snapshots.');
|
|
667
|
+
}
|
|
668
|
+
return config;
|
|
669
|
+
}
|
|
670
|
+
async buildSnapshotTip(instanceId) {
|
|
671
|
+
if (this.isJsonEnabled() || this.parsedFlags?.['no-snapshot'])
|
|
672
|
+
return '';
|
|
673
|
+
try {
|
|
674
|
+
const snapshot = await this.client.xcodeInstances.getCache(instanceId, {
|
|
675
|
+
timeout: 2000,
|
|
676
|
+
maxRetries: 0,
|
|
677
|
+
});
|
|
678
|
+
if (snapshot.config)
|
|
679
|
+
return '';
|
|
680
|
+
}
|
|
681
|
+
catch {
|
|
682
|
+
// Unknown snapshot state must not change the build result or suggest a redundant key.
|
|
683
|
+
return '';
|
|
684
|
+
}
|
|
685
|
+
return '\nTip: Use --snapshot-key <project-key> for disk snapshots to build under 60 seconds!';
|
|
631
686
|
}
|
|
632
687
|
/**
|
|
633
688
|
* Blocks while a freshly created instance restores its cache, printing each phase as it
|
|
@@ -808,7 +863,7 @@ class BaseCommand extends core_1.Command {
|
|
|
808
863
|
return status ? { target, client, status } : undefined;
|
|
809
864
|
}
|
|
810
865
|
/** Save the workspace preference and switch its existing sandbox when available. */
|
|
811
|
-
async setPreferredXcodeVersion(version, providedId) {
|
|
866
|
+
async setPreferredXcodeVersion(version, providedId, renderJson = (result) => this.outputJson(result)) {
|
|
812
867
|
const previous = (0, config_1.loadXcodeVersionPreference)();
|
|
813
868
|
// Keep the workspace preference on transient failures, but reject versions the node cannot provide.
|
|
814
869
|
const record = () => {
|
|
@@ -840,13 +895,13 @@ class BaseCommand extends core_1.Command {
|
|
|
840
895
|
record();
|
|
841
896
|
if (!target || !result) {
|
|
842
897
|
if (this.isJsonEnabled())
|
|
843
|
-
|
|
898
|
+
renderJson({ preferred: version });
|
|
844
899
|
else
|
|
845
900
|
this.output(`No sandbox instance found${this.scopeSuffix()}; the next one uses it.`);
|
|
846
901
|
return;
|
|
847
902
|
}
|
|
848
903
|
if (this.isJsonEnabled()) {
|
|
849
|
-
|
|
904
|
+
renderJson({ instanceId: target.id, preferred: version, ...result });
|
|
850
905
|
return;
|
|
851
906
|
}
|
|
852
907
|
const verb = result.alreadyBound ? 'already uses' : 'now uses';
|
|
@@ -1092,11 +1147,18 @@ class BaseCommand extends core_1.Command {
|
|
|
1092
1147
|
}
|
|
1093
1148
|
async resolveGradleClient(target) {
|
|
1094
1149
|
if (target.apiUrl && target.token) {
|
|
1095
|
-
return this.client.gradleInstances.createClient({
|
|
1150
|
+
return this.client.gradleInstances.createClient({
|
|
1151
|
+
apiUrl: target.apiUrl,
|
|
1152
|
+
token: target.token,
|
|
1153
|
+
logLevel: this.shouldSuppressInfo() ? 'warn' : 'info',
|
|
1154
|
+
});
|
|
1096
1155
|
}
|
|
1097
1156
|
const instance = await this.client.gradleInstances.get(target.id);
|
|
1098
1157
|
(0, config_1.registerCreatedInstance)(instance);
|
|
1099
|
-
return this.client.gradleInstances.createClient({
|
|
1158
|
+
return this.client.gradleInstances.createClient({
|
|
1159
|
+
instance,
|
|
1160
|
+
logLevel: this.shouldSuppressInfo() ? 'warn' : 'info',
|
|
1161
|
+
});
|
|
1100
1162
|
}
|
|
1101
1163
|
async resolveBuildToolClient(platform, id, mode = 'work') {
|
|
1102
1164
|
if (platform === 'gradle') {
|
|
@@ -1132,7 +1194,7 @@ BaseCommand.baseFlags = {
|
|
|
1132
1194
|
env: 'LIM_API_KEY',
|
|
1133
1195
|
}),
|
|
1134
1196
|
json: core_1.Flags.boolean({
|
|
1135
|
-
description: 'Output
|
|
1197
|
+
description: 'Output JSON for automation. Streams and watch modes emit newline-delimited JSON. Human-readable diagnostics go to stderr.',
|
|
1136
1198
|
default: false,
|
|
1137
1199
|
}),
|
|
1138
1200
|
quiet: core_1.Flags.boolean({
|