@swmansion/argent 0.15.1-next.6 → 0.15.1-next.7

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/cli-cmds.mjs CHANGED
@@ -6324,6 +6324,7 @@ var FAILURE_CODES = {
6324
6324
  CHROMIUM_ELECTRON_EXITED_BEFORE_READY: "CHROMIUM_ELECTRON_EXITED_BEFORE_READY",
6325
6325
  KEYBOARD_KEY_UNSUPPORTED: "KEYBOARD_KEY_UNSUPPORTED",
6326
6326
  KEYBOARD_CHARACTER_UNSUPPORTED: "KEYBOARD_CHARACTER_UNSUPPORTED",
6327
+ SECRET_PLACEHOLDER_UNKNOWN: "SECRET_PLACEHOLDER_UNKNOWN",
6327
6328
  SCREENSHOT_DIFF_INPUT_INVALID: "SCREENSHOT_DIFF_INPUT_INVALID",
6328
6329
  BOOT_DEVICE_TARGET_SELECTION_INVALID: "BOOT_DEVICE_TARGET_SELECTION_INVALID",
6329
6330
  BOOT_IOS_UNSUPPORTED_HOST: "BOOT_IOS_UNSUPPORTED_HOST",
@@ -16231,6 +16231,7 @@ var FAILURE_CODES = {
16231
16231
  CHROMIUM_ELECTRON_EXITED_BEFORE_READY: "CHROMIUM_ELECTRON_EXITED_BEFORE_READY",
16232
16232
  KEYBOARD_KEY_UNSUPPORTED: "KEYBOARD_KEY_UNSUPPORTED",
16233
16233
  KEYBOARD_CHARACTER_UNSUPPORTED: "KEYBOARD_CHARACTER_UNSUPPORTED",
16234
+ SECRET_PLACEHOLDER_UNKNOWN: "SECRET_PLACEHOLDER_UNKNOWN",
16234
16235
  SCREENSHOT_DIFF_INPUT_INVALID: "SCREENSHOT_DIFF_INPUT_INVALID",
16235
16236
  BOOT_DEVICE_TARGET_SELECTION_INVALID: "BOOT_DEVICE_TARGET_SELECTION_INVALID",
16236
16237
  BOOT_IOS_UNSUPPORTED_HOST: "BOOT_IOS_UNSUPPORTED_HOST",
@@ -17750,6 +17750,7 @@ var FAILURE_CODES = {
17750
17750
  CHROMIUM_ELECTRON_EXITED_BEFORE_READY: "CHROMIUM_ELECTRON_EXITED_BEFORE_READY",
17751
17751
  KEYBOARD_KEY_UNSUPPORTED: "KEYBOARD_KEY_UNSUPPORTED",
17752
17752
  KEYBOARD_CHARACTER_UNSUPPORTED: "KEYBOARD_CHARACTER_UNSUPPORTED",
17753
+ SECRET_PLACEHOLDER_UNKNOWN: "SECRET_PLACEHOLDER_UNKNOWN",
17753
17754
  SCREENSHOT_DIFF_INPUT_INVALID: "SCREENSHOT_DIFF_INPUT_INVALID",
17754
17755
  BOOT_DEVICE_TARGET_SELECTION_INVALID: "BOOT_DEVICE_TARGET_SELECTION_INVALID",
17755
17756
  BOOT_IOS_UNSUPPORTED_HOST: "BOOT_IOS_UNSUPPORTED_HOST",
@@ -18981,6 +18982,14 @@ var DEFAULT_DELAY_MS = 1400;
18981
18982
  function autoScreenshotEnabled(options) {
18982
18983
  return !isFlagEnabled("disable-auto-screenshot", options);
18983
18984
  }
18985
+ var SECRET_PLACEHOLDER_MARKER = "{{secret:";
18986
+ function containsSecretPlaceholder(args) {
18987
+ try {
18988
+ return JSON.stringify(args)?.includes(SECRET_PLACEHOLDER_MARKER) ?? false;
18989
+ } catch {
18990
+ return true;
18991
+ }
18992
+ }
18984
18993
  function getUdidFromArgs(args) {
18985
18994
  if (args && typeof args === "object" && "udid" in args && typeof args.udid === "string") {
18986
18995
  return args.udid;
@@ -19209,7 +19218,15 @@ async function startMcpServer(options) {
19209
19218
  content = await toMcpContent(result, outputHint, ctx, params.arguments);
19210
19219
  }
19211
19220
  const udid = getUdidFromArgs(params.arguments);
19212
- if (autoScreenshotOn && udid && shouldAutoScreenshot(params.name)) {
19221
+ if (autoScreenshotOn && udid && shouldAutoScreenshot(params.name) && containsSecretPlaceholder(params.arguments)) {
19222
+ content = [
19223
+ ...content,
19224
+ {
19225
+ type: "text",
19226
+ text: "Auto-screenshot skipped: the input contains a {{secret:\u2026}} placeholder, and a screenshot of this screen could reveal the typed secret. Submit or navigate away first, then verify the resulting screen as usual."
19227
+ }
19228
+ ];
19229
+ } else if (autoScreenshotOn && udid && shouldAutoScreenshot(params.name)) {
19213
19230
  const maxWaitMs = getAutoScreenshotDelayMs(params.name);
19214
19231
  if (maxWaitMs > 0) {
19215
19232
  try {
@@ -404,6 +404,7 @@ var init_failure_codes = __esm({
404
404
  CHROMIUM_ELECTRON_EXITED_BEFORE_READY: "CHROMIUM_ELECTRON_EXITED_BEFORE_READY",
405
405
  KEYBOARD_KEY_UNSUPPORTED: "KEYBOARD_KEY_UNSUPPORTED",
406
406
  KEYBOARD_CHARACTER_UNSUPPORTED: "KEYBOARD_CHARACTER_UNSUPPORTED",
407
+ SECRET_PLACEHOLDER_UNKNOWN: "SECRET_PLACEHOLDER_UNKNOWN",
407
408
  SCREENSHOT_DIFF_INPUT_INVALID: "SCREENSHOT_DIFF_INPUT_INVALID",
408
409
  BOOT_DEVICE_TARGET_SELECTION_INVALID: "BOOT_DEVICE_TARGET_SELECTION_INVALID",
409
410
  BOOT_IOS_UNSUPPORTED_HOST: "BOOT_IOS_UNSUPPORTED_HOST",
@@ -126687,6 +126688,54 @@ Fails if the device backend is not reachable \u2014 the simulator-server for iOS
126687
126688
  // ../tool-server/src/tools/keyboard/index.ts
126688
126689
  init_zod();
126689
126690
 
126691
+ // ../tool-server/src/utils/secrets.ts
126692
+ init_src();
126693
+ var SECRET_ENV_PREFIX = "ARGENT_SECRET_";
126694
+ var SECRET_PLACEHOLDER_MARKER = "{{secret:";
126695
+ var PLACEHOLDER_RE = /\{\{secret:([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
126696
+ function availableSecretNames(env = process.env) {
126697
+ return Object.keys(env).filter((k) => k.startsWith(SECRET_ENV_PREFIX) && env[k] !== void 0).map((k) => k.slice(SECRET_ENV_PREFIX.length)).sort();
126698
+ }
126699
+ var REDUNDANT_PREFIX_RE = /^argent_secret_/i;
126700
+ function resolveSecretPlaceholders(text, env = process.env) {
126701
+ const secrets = [];
126702
+ const resolved = text.replace(PLACEHOLDER_RE, (placeholder, rawName) => {
126703
+ let name = rawName;
126704
+ let value = env[SECRET_ENV_PREFIX + name];
126705
+ if (value === void 0 && REDUNDANT_PREFIX_RE.test(name)) {
126706
+ name = name.replace(REDUNDANT_PREFIX_RE, "");
126707
+ value = env[SECRET_ENV_PREFIX + name];
126708
+ }
126709
+ if (value === void 0) {
126710
+ const names = availableSecretNames(env);
126711
+ throw new InvalidToolInputError(
126712
+ `Unknown secret "${rawName}" \u2014 no ${SECRET_ENV_PREFIX}${name} environment variable is set on the machine running the tool-server. Available secrets: ${names.length ? names.join(", ") : "(none)"}. To make it available, ask the user to export ${SECRET_ENV_PREFIX}${name} in the tool-server's environment \u2014 never ask the user for the secret value itself.`,
126713
+ {
126714
+ error_code: FAILURE_CODES.SECRET_PLACEHOLDER_UNKNOWN,
126715
+ failure_stage: "secret_placeholder_resolution",
126716
+ error_kind: "validation"
126717
+ }
126718
+ );
126719
+ }
126720
+ if (!secrets.some((s) => s.name === name)) secrets.push({ name, value });
126721
+ return value;
126722
+ });
126723
+ return { text: resolved, secrets };
126724
+ }
126725
+ function redactSecretsFromError(err, secrets) {
126726
+ const scrub = (s) => secrets.reduce(
126727
+ (acc, { name, value }) => value ? acc.split(value).join(`${SECRET_PLACEHOLDER_MARKER}${name}}}`) : acc,
126728
+ s
126729
+ );
126730
+ if (err instanceof Error) {
126731
+ err.message = scrub(err.message);
126732
+ if (err.stack) err.stack = scrub(err.stack);
126733
+ return err;
126734
+ }
126735
+ if (typeof err === "string") return scrub(err);
126736
+ return err;
126737
+ }
126738
+
126690
126739
  // ../tool-server/src/tools/keyboard/simulator-server-keys.ts
126691
126740
  init_src();
126692
126741
 
@@ -127165,7 +127214,9 @@ var zodSchema24 = external_exports.object({
127165
127214
  udid: external_exports.string().describe(
127166
127215
  "Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id)."
127167
127216
  ),
127168
- text: external_exports.string().optional().describe("Text to type character by character. Handles uppercase and common punctuation."),
127217
+ text: external_exports.string().optional().describe(
127218
+ 'Text to type character by character. Handles uppercase and common punctuation. To type a credential without its plaintext ever entering your context, use a secret placeholder: `{{secret:<NAME>}}` types the value of the `ARGENT_SECRET_<NAME>` environment variable set on the machine running the tool-server \u2014 e.g. text: "{{secret:APP_PASSWORD}}" types the value of `ARGENT_SECRET_APP_PASSWORD`. Only env vars with the `ARGENT_SECRET_` prefix are resolvable. Placeholders can be embedded in longer text and are never echoed back resolved. If the secret you need is not set, ask the user to export it as `ARGENT_SECRET_<NAME>` and restart the session \u2014 NEVER ask the user to paste the secret value into the conversation.'
127219
+ ),
127169
127220
  key: external_exports.string().optional().describe(
127170
127221
  "Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12. When combined with `text`, the key is pressed AFTER the text is typed (so text + enter types and submits). Not supported on TV targets \u2014 move focus with `tv-remote` (up/down/left/right) instead."
127171
127222
  ),
@@ -127181,12 +127232,21 @@ var capability16 = {
127181
127232
  vega: { vvd: true }
127182
127233
  };
127183
127234
  function createKeyboardTool(registry2) {
127235
+ const dispatch = dispatchByPlatform({
127236
+ toolId: "keyboard",
127237
+ capability: capability16,
127238
+ ios: makeIosImpl3(registry2),
127239
+ iosRemote: makeIosRemoteImpl(registry2),
127240
+ android: makeAndroidImpl(registry2),
127241
+ chromium: makeChromiumImpl(registry2),
127242
+ vega: vegaImpl4
127243
+ });
127184
127244
  return {
127185
127245
  id: "keyboard",
127186
127246
  description: `Type text or press special keys on the device (iOS simulator, Android emulator or device, Chromium app, Vega Virtual Device, or Apple TV / Android TV) using keyboard events.
127187
127247
  Use when you need to enter text or trigger a named key such as enter, escape, or arrow keys. On Vega and Apple TV / Android TV, prefer the remote tools for D-pad navigation; use keyboard to type into a focused text field (e.g. a search or login box).
127188
127248
  Returns { typed: string, keys: number }. Fails if an unsupported key name is provided or the device's input backend is not reachable.
127189
- - text: types a string (supports uppercase, digits, common punctuation)
127249
+ - text: types a string (supports uppercase, digits, common punctuation). To type a credential, use \`{{secret:<NAME>}}\` \u2014 resolved server-side from the \`ARGENT_SECRET_<NAME>\` env var (prefix mandatory; \`{{secret:APP_PASSWORD}}\` \u2194 \`ARGENT_SECRET_APP_PASSWORD\`), so the plaintext never enters agent context; the result echoes the placeholder, not the value, and the after-typing auto-screenshot is skipped.
127190
127250
  - key: presses a single named key (enter, escape, backspace, tab, arrow-up/down/left/right, f1\u2013f12) \u2014 NOT supported on TV targets; move focus with \`tv-remote\` instead.
127191
127251
  On a TV target (runtimeKind 'tv') only \`text\` applies \u2014 focus a text field first (with \`tv-remote\`), then type into it (injected HID keyboard on Apple TV, \`adb input text\` on Android TV).
127192
127252
  Provide text, key, or both \u2014 when both are given, the text is typed first and the key is pressed after it (text + key:"enter" types and submits).`,
@@ -127197,15 +127257,17 @@ Provide text, key, or both \u2014 when both are given, the text is typed first a
127197
127257
  // simulator-server, CDP, or Vega adb), since distinguishing a TV target is
127198
127258
  // async and a tvOS udid must never resolve simulator-server.
127199
127259
  services: () => ({}),
127200
- execute: dispatchByPlatform({
127201
- toolId: "keyboard",
127202
- capability: capability16,
127203
- ios: makeIosImpl3(registry2),
127204
- iosRemote: makeIosRemoteImpl(registry2),
127205
- android: makeAndroidImpl(registry2),
127206
- chromium: makeChromiumImpl(registry2),
127207
- vega: vegaImpl4
127208
- })
127260
+ execute: async (services, params, options) => {
127261
+ if (params.text === void 0) return dispatch(services, params, options);
127262
+ const { text, secrets } = resolveSecretPlaceholders(params.text);
127263
+ if (secrets.length === 0) return dispatch(services, params, options);
127264
+ try {
127265
+ const result = await dispatch(services, { ...params, text }, options);
127266
+ return { ...result, typed: params.text };
127267
+ } catch (err) {
127268
+ throw redactSecretsFromError(err, secrets);
127269
+ }
127270
+ }
127209
127271
  };
127210
127272
  }
127211
127273
 
@@ -133622,6 +133684,7 @@ Allowed tools and their args (udid is auto-injected, do NOT include it in args):
133622
133684
  gesture-rotate: { centerX: number, centerY: number, radius: number, startAngle: number, endAngle: number, durationMs?: number } [ios only]
133623
133685
  button: { button: "home"|"back"|"power"|"volumeUp"|"volumeDown"|"appSwitch"|"actionButton" } [ios/android]
133624
133686
  keyboard: { text?: string, key?: string, delayMs?: number } (key pressed after text; TV: text only) [ios/android/chromium/vega/tv]
133687
+ text supports {{secret:<NAME>}} placeholders, resolved server-side from ARGENT_SECRET_<NAME> env vars (prefix mandatory) \u2014 credentials never enter agent context
133625
133688
  rotate: { orientation: "Portrait"|"LandscapeLeft"|"LandscapeRight"|"PortraitUpsideDown" } [ios/android]
133626
133689
  tv-remote: { button: <remote button | array of them>, repeat?: number } [apple tv/android tv/vega]
133627
133690
  buttons: up/down/left/right/select/back/home/menu/playPause (+ rewind/fastForward/next/previous/volumeUp/volumeDown/mute \u2014 work on Android TV and Vega; rejected on the Apple TV simulator)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.15.1-next.6",
3
+ "version": "0.15.1-next.7",
4
4
  "description": "MCP server for iOS Simulator and Android Emulator control",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -60,6 +60,8 @@ For a custom poll interval or bundleId, drop to an explicit `- tool: await-ui-el
60
60
 
61
61
  `type` presses Enter after typing to commit the value and dismiss the keyboard, so it can't cover later targets. For a chained form whose fields feed one explicit submit — e.g. email then password then a `tap: "Log in"` — set `submit: false` on the intermediate fields so a premature Enter doesn't fire the form early: `type: { into: password, text: "hunter2", submit: false }`.
62
62
 
63
+ Never record a real credential into a flow — the YAML is committed to the repo. Use a secret placeholder instead: `type: { into: password, text: "{{secret:APP_PASSWORD}}" }`. The placeholder is stored verbatim (the YAML stays secret-free) and is resolved at run time by the tool-server from the `ARGENT_SECRET_APP_PASSWORD` environment variable — including agent-less `argent flow run` in CI, where the variable comes from the job's secrets.
64
+
63
65
  `scroll-to` takes an optional `direction` (`up` | `down` | `left` | `right`, default `down` — so the common case is just `- scroll-to: <selector>`) and optionally a `within: <selector>` that anchors the scroll inside a specific container — required to drive a **nested** scroller (e.g. a horizontal carousel inside a vertical list), since the device can't be asked which container to scroll. It scrolls in bounded momentum-free increments, re-checks after each, and stops if a scroll reveals nothing new (end of the container). `tap`/`type` do **not** scroll — add a `scroll-to` before any target that may be off-screen. It's a no-op when the target is already visible, so a defensive `scroll-to` costs nothing on replay and keeps the flow working on smaller screens.
64
66
 
65
67
  ### TV targets (Vega)
@@ -171,6 +171,18 @@ Values: `home`, `back`, `power`, `volumeUp`, `volumeDown`, `appSwitch`, `actionB
171
171
 
172
172
  Special keys: `enter`, `escape`, `backspace`, `tab`, `space`, `arrow-up`, `arrow-down`, `arrow-left`, `arrow-right`, `f1`–`f12`. Optional: `"delayMs": 100` between keystrokes (default 50ms) — applies to the iOS simulator and Chromium; it is ignored on Android phones/tablets (typed via `adb input text`, no per-key cadence), on Vega, and on TV targets.
173
173
 
174
+ **Typing secrets.** To enter a credential without its plaintext ever entering your context, transcript, or logs, use a secret placeholder in `text` (works in `keyboard`, `paste`, `run-sequence` keyboard steps, and flow `type` steps):
175
+
176
+ ```json
177
+ { "udid": "<UDID>", "text": "{{secret:APP_PASSWORD}}", "key": "enter" }
178
+ ```
179
+
180
+ The placeholder is resolved on the machine running the tool-server from the `ARGENT_SECRET_<NAME>` environment variable (here `ARGENT_SECRET_APP_PASSWORD`) — the CI-native pattern: expose the secret under that prefix in the environment that starts the tool-server. Rules:
181
+
182
+ - The result echoes the placeholder, never the value. An unknown name fails with the list of available secret _names_.
183
+ - The auto-screenshot after the call is skipped so the typed value cannot re-enter your context as pixels. Do **not** `describe` or `screenshot` a non-secure field you just filled with a secret — submit or navigate away first, then verify the resulting screen.
184
+ - Only `ARGENT_SECRET_*` variables are resolvable; never ask the user to paste a secret value into the conversation — ask them to export the env var instead.
185
+
174
186
  ### rotate — Change orientation
175
187
 
176
188
  ```json