@hasna/machines 0.0.26 → 0.0.27

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/README.md CHANGED
@@ -130,6 +130,7 @@ machines screen machine005 --print # print the vnc:// URL instead of ope
130
130
  machines screen machine005 --json # full resolution detail (route, confidence, user)
131
131
  machines screen --all # open every reachable machine
132
132
  machines screen --all --print # list resolved vnc:// URLs for the whole fleet
133
+ machines screen-credentials --all --check-secret
133
134
  ```
134
135
 
135
136
  Enable Remote Management / Screen Sharing on a fresh macOS machine over SSH
@@ -137,12 +138,20 @@ Enable Remote Management / Screen Sharing on a fresh macOS machine over SSH
137
138
  Screen Sharing.app and Apple Remote Desktop):
138
139
 
139
140
  ```bash
140
- machines screen-enable --machine machine005 --user jo --vnc-password steaua17
141
+ secrets set hasna/xyz/opensource/machines/prod/screen-machine005-vnc-password "$VNC_PASSWORD" --type password
142
+ machines screen-enable --machine machine005 --user jo \
143
+ --vnc-password-secret hasna/xyz/opensource/machines/prod/screen-machine005-vnc-password
141
144
  machines screen-enable --machine machine005 --user jo --print # show the SSH command, don't run it
142
145
  ```
143
146
 
144
- `--vnc-password` is truncated to 8 characters by the legacy VNC protocol. The
145
- user comes from the manifest (`metadata.user`) when present, or `--user`.
147
+ The legacy VNC protocol honors only the first 8 password characters. The
148
+ password is read through the `secrets` CLI and piped over SSH stdin; it is not
149
+ embedded in generated command text. If `--vnc-password-secret` is omitted,
150
+ machines defaults to
151
+ `hasna/xyz/opensource/machines/prod/screen-<machine>-vnc-password`. The user
152
+ comes from the manifest (`metadata.user`) when present, or `--user`.
153
+ `screen-credentials` verifies the resolved user and secret key for a machine or
154
+ the full fleet without printing secret values.
146
155
 
147
156
  Consumers that need repo paths can resolve trust-aware workspace mappings
148
157
  without importing the full machines app:
package/dist/cli/index.js CHANGED
@@ -9153,15 +9153,32 @@ function listPorts(machineId) {
9153
9153
  }
9154
9154
 
9155
9155
  // src/commands/screen.ts
9156
+ var DEFAULT_SCREEN_SECRET_NAMESPACE = "hasna/xyz/opensource/machines/prod";
9156
9157
  function shellQuote6(value) {
9157
9158
  return `'${value.replace(/'/g, "'\\''")}'`;
9158
9159
  }
9160
+ function shellCommand2(command) {
9161
+ return command.map(shellQuote6).join(" ");
9162
+ }
9163
+ function metadataString2(metadata, keys) {
9164
+ if (!metadata)
9165
+ return null;
9166
+ for (const key of keys) {
9167
+ const value = metadata[key];
9168
+ if (typeof value === "string" && value.trim())
9169
+ return value.trim();
9170
+ }
9171
+ return null;
9172
+ }
9159
9173
  function splitTarget(target) {
9160
9174
  const at = target.indexOf("@");
9161
9175
  if (at === -1)
9162
9176
  return [null, target];
9163
9177
  return [target.slice(0, at), target.slice(at + 1)];
9164
9178
  }
9179
+ function defaultScreenPasswordSecretKey(machineId) {
9180
+ return `${DEFAULT_SCREEN_SECRET_NAMESPACE}/screen-${machineId}-vnc-password`;
9181
+ }
9165
9182
  function resolveScreenTarget(machineId, options = {}) {
9166
9183
  const resolved = resolveMachineRoute(machineId, options);
9167
9184
  if (!resolved.ok || !resolved.target) {
@@ -9187,15 +9204,60 @@ function resolveScreenTarget(machineId, options = {}) {
9187
9204
  warnings: resolved.warnings
9188
9205
  };
9189
9206
  }
9190
- function buildScreenEnableRemoteCommand(user, vncPassword) {
9207
+ function resolveScreenCredentials(machineId, options = {}) {
9208
+ const topology = options.topology ?? discoverMachineTopology(options);
9209
+ const screen = resolveScreenTarget(machineId, { ...options, topology });
9210
+ const entry = topology.machines.find((machine) => machine.machine_id === screen.machineId);
9211
+ const metadata = entry?.metadata;
9212
+ const metadataUser = metadataString2(metadata, ["screenUser", "screen_user", "user", "username"]);
9213
+ const metadataPasswordSecret = metadataString2(metadata, [
9214
+ "screenPasswordSecret",
9215
+ "screen_password_secret",
9216
+ "screenVncPasswordSecret",
9217
+ "screen_vnc_password_secret",
9218
+ "vncPasswordSecret",
9219
+ "vnc_password_secret"
9220
+ ]);
9221
+ const user = options.user ?? screen.user ?? metadataUser;
9222
+ const passwordSecretKey = options.passwordSecretKey ?? metadataPasswordSecret ?? defaultScreenPasswordSecretKey(screen.machineId);
9223
+ return {
9224
+ machineId: screen.machineId,
9225
+ user: user ?? null,
9226
+ userSource: options.user ? "option" : screen.user ? "route" : metadataUser ? "metadata" : "missing",
9227
+ passwordSecretKey,
9228
+ passwordSecretSource: options.passwordSecretKey ? "option" : metadataPasswordSecret ? "metadata" : "default"
9229
+ };
9230
+ }
9231
+ function buildScreenEnableRemoteCommandFromStdin(user) {
9191
9232
  const kickstart = "/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart";
9192
- const lines = [
9193
- `dseditgroup -o edit -a ${shellQuote6(user)} -t user com.apple.access_screensharing 2>/dev/null || true`,
9233
+ const script = [
9234
+ "set -euo pipefail",
9235
+ 'user="$1"',
9236
+ "IFS= read -r vnc_pw",
9237
+ 'if [ -z "$vnc_pw" ]; then echo "missing VNC password on stdin" >&2; exit 1; fi',
9238
+ `kickstart=${shellQuote6(kickstart)}`,
9239
+ 'dseditgroup -o edit -a "$user" -t user com.apple.access_screensharing 2>/dev/null || true',
9194
9240
  "defaults write /Library/Preferences/com.apple.RemoteManagement AllowSRPForNetworkNodes -bool true",
9195
- `${kickstart} -configure -clientopts -setvnclegacy -vnclegacy yes -setvncpw -vncpw ${shellQuote6(vncPassword)}`,
9196
- `${kickstart} -activate -configure -access -on -users ${shellQuote6(user)} -privs -all -restart -agent -menu`
9197
- ];
9198
- return lines.join(" && ");
9241
+ '"$kickstart" -configure -clientopts -setvnclegacy -vnclegacy yes -setvncpw -vncpw "$vnc_pw"',
9242
+ '"$kickstart" -activate -configure -access -on -users "$user" -privs -all -restart -agent -menu'
9243
+ ].join(`
9244
+ `);
9245
+ return `sudo -n -p '' /bin/bash -c ${shellQuote6(script)} -- ${shellQuote6(user)}`;
9246
+ }
9247
+ function buildScreenEnableCommand(machineId, options = {}) {
9248
+ const credentials = resolveScreenCredentials(machineId, options);
9249
+ if (!credentials.user) {
9250
+ throw new Error(`No screen-sharing user known for ${machineId}; pass --user <name> or set metadata.user in the manifest.`);
9251
+ }
9252
+ const secretsCommand = options.secretsCommand || "secrets";
9253
+ const remoteCommand = buildScreenEnableRemoteCommandFromStdin(credentials.user);
9254
+ return {
9255
+ machineId: credentials.machineId,
9256
+ user: credentials.user,
9257
+ passwordSecretKey: credentials.passwordSecretKey,
9258
+ remoteCommand,
9259
+ command: `${shellCommand2([secretsCommand, "get", credentials.passwordSecretKey])} | ${buildSshCommand(machineId, remoteCommand, options)}`
9260
+ };
9199
9261
  }
9200
9262
 
9201
9263
  // src/commands/sync.ts
@@ -11221,6 +11283,19 @@ function renderSelfTestResult(result) {
11221
11283
  ].join(`
11222
11284
  `);
11223
11285
  }
11286
+ function checkSecretPresence(secretsCommand, key) {
11287
+ const result = Bun.spawnSync([secretsCommand, "get", key], {
11288
+ stdout: "pipe",
11289
+ stderr: "pipe",
11290
+ env: process.env
11291
+ });
11292
+ const stdout = result.stdout.toString().trim();
11293
+ return {
11294
+ checked: true,
11295
+ present: result.exitCode === 0 && stdout.length > 0,
11296
+ error: result.exitCode === 0 ? undefined : result.stderr.toString().trim() || `secrets get exited ${result.exitCode}`
11297
+ };
11298
+ }
11224
11299
  function parseCommandSpec(value) {
11225
11300
  const [command, expectedVersion] = value.split(":");
11226
11301
  return {
@@ -11727,23 +11802,58 @@ program2.command("screen").description("Open Screen Sharing (VNC) to a machine u
11727
11802
  execFileSync("open", [resolved.url], { stdio: "ignore" });
11728
11803
  console.log(`Opening Screen Sharing \u2192 ${resolved.url} (route: ${resolved.route})`);
11729
11804
  });
11730
- program2.command("screen-enable").description("Enable Remote Management / Screen Sharing on a macOS machine over SSH").requiredOption("--machine <id>", "Machine identifier").option("--user <user>", "macOS user to grant screen-sharing (overrides manifest)").option("--vnc-password <pw>", "Legacy VNC password (<=8 chars honored)", "").option("--print", "Print the remote command instead of running it", false).action((options) => {
11731
- const screen = resolveScreenTarget(options.machine);
11732
- const user = options.user ?? screen.user;
11733
- if (!user) {
11734
- console.error(`No SSH user known for ${options.machine}; pass --user <name> or set metadata.user in the manifest.`);
11805
+ program2.command("screen-credentials").description("Inspect screen-sharing user and password secret references without printing secrets").option("--machine <id>", "Machine identifier").option("--all", "Inspect every discovered machine", false).option("--check-secret", "Check whether the password secret exists in the local secrets vault", false).option("--secrets-command <command>", "Secrets CLI command to inspect", "secrets").option("--no-tailscale", "Skip tailscale status probing").option("-j, --json", "Print JSON output", false).action((options) => {
11806
+ const topology = discoverMachineTopology({ includeTailscale: options.tailscale !== false });
11807
+ const machineIds = options.all ? topology.machines.map((machine) => machine.machine_id) : [options.machine].filter((machine) => Boolean(machine));
11808
+ if (machineIds.length === 0) {
11809
+ console.error("Provide --machine <id> or --all");
11735
11810
  process.exitCode = 1;
11736
11811
  return;
11737
11812
  }
11738
- const vncPw = (options.vncPassword || "").slice(0, 8);
11739
- const remoteCmd = buildScreenEnableRemoteCommand(user, vncPw);
11740
- const sshCmd = buildSshCommand(options.machine, `sudo -p '' bash -c ${JSON.stringify(remoteCmd)}`);
11813
+ const results = machineIds.map((machineId) => {
11814
+ try {
11815
+ const credentials = resolveScreenCredentials(machineId, { topology });
11816
+ const secret = options.checkSecret ? checkSecretPresence(options.secretsCommand, credentials.passwordSecretKey) : { checked: false, present: null };
11817
+ return { ok: true, ...credentials, passwordSecret: secret };
11818
+ } catch (error) {
11819
+ return { ok: false, machineId, error: error instanceof Error ? error.message : String(error) };
11820
+ }
11821
+ });
11822
+ const hasFailures = results.some((result) => !result.ok || result.ok && result.passwordSecret.checked && !result.passwordSecret.present);
11823
+ if (options.json) {
11824
+ console.log(JSON.stringify(results, null, 2));
11825
+ if (hasFailures)
11826
+ process.exitCode = 1;
11827
+ return;
11828
+ }
11829
+ for (const result of results) {
11830
+ if (!result.ok) {
11831
+ console.log(`\u2717 ${result.machineId.padEnd(14)} ${result.error}`);
11832
+ continue;
11833
+ }
11834
+ const secret = result.passwordSecret.checked ? result.passwordSecret.present ? source_default.green("present") : source_default.red("missing") : source_default.yellow("unchecked");
11835
+ console.log(`${result.machineId.padEnd(14)} user=${result.user ?? "(missing)"} passwordSecret=${result.passwordSecretKey} (${secret})`);
11836
+ }
11837
+ if (hasFailures)
11838
+ process.exitCode = 1;
11839
+ });
11840
+ program2.command("screen-enable").description("Enable Remote Management / Screen Sharing on a macOS machine over SSH").requiredOption("--machine <id>", "Machine identifier").option("--user <user>", "macOS user to grant screen-sharing (overrides manifest)").option("--vnc-password-secret <key>", "Secret key containing the legacy VNC password").option("--secrets-command <command>", "Secrets CLI command to read the password", "secrets").option("--vnc-password <pw>", "Deprecated: use --vnc-password-secret instead", "").option("--print", "Print the remote command instead of running it", false).action((options) => {
11841
+ if (options.vncPassword) {
11842
+ console.error("Direct --vnc-password values are not accepted. Store the password with `secrets set` and pass --vnc-password-secret.");
11843
+ process.exitCode = 1;
11844
+ return;
11845
+ }
11846
+ const plan = buildScreenEnableCommand(options.machine, {
11847
+ user: options.user,
11848
+ passwordSecretKey: options.vncPasswordSecret,
11849
+ secretsCommand: options.secretsCommand
11850
+ });
11741
11851
  if (options.print) {
11742
- console.log(sshCmd);
11852
+ console.log(plan.command);
11743
11853
  return;
11744
11854
  }
11745
- console.log(`Run this to enable Screen Sharing on ${options.machine} (will prompt for sudo on the target):`);
11746
- console.log(` ${sshCmd}`);
11855
+ console.log(`Run this to enable Screen Sharing on ${options.machine} (password comes from ${plan.passwordSecretKey}):`);
11856
+ console.log(` ${plan.command}`);
11747
11857
  });
11748
11858
  program2.command("ports").description("List listening ports on a machine").option("--machine <id>", "Machine identifier").option("-j, --json", "Print JSON output", false).action((options) => {
11749
11859
  const result = listPorts(options.machine);
@@ -1,4 +1,5 @@
1
1
  import { type MachineRouteOptions, type MachineRouteKind, type MachineRouteConfidence } from "../topology.js";
2
+ export declare const DEFAULT_SCREEN_SECRET_NAMESPACE = "hasna/xyz/opensource/machines/prod";
2
3
  export interface ResolvedScreenTarget {
3
4
  machineId: string;
4
5
  user: string | null;
@@ -8,12 +9,35 @@ export interface ResolvedScreenTarget {
8
9
  confidence: MachineRouteConfidence;
9
10
  warnings: string[];
10
11
  }
12
+ export interface ScreenCredentialResolution {
13
+ machineId: string;
14
+ user: string | null;
15
+ userSource: "option" | "route" | "metadata" | "missing";
16
+ passwordSecretKey: string;
17
+ passwordSecretSource: "option" | "metadata" | "default";
18
+ }
19
+ export interface ScreenEnableCommandPlan {
20
+ machineId: string;
21
+ user: string;
22
+ passwordSecretKey: string;
23
+ remoteCommand: string;
24
+ command: string;
25
+ }
26
+ export interface ScreenCredentialOptions extends MachineRouteOptions {
27
+ user?: string;
28
+ passwordSecretKey?: string;
29
+ }
30
+ export interface ScreenEnableCommandOptions extends ScreenCredentialOptions {
31
+ secretsCommand?: string;
32
+ }
33
+ export declare function defaultScreenPasswordSecretKey(machineId: string): string;
11
34
  /**
12
35
  * Resolve the best screen-sharing (VNC) target for a machine.
13
36
  * Prefers the live LAN route over Tailscale (lower latency for screen sharing),
14
37
  * and always produces a `vnc://user@host` URL when a user is known.
15
38
  */
16
39
  export declare function resolveScreenTarget(machineId: string, options?: MachineRouteOptions): ResolvedScreenTarget;
40
+ export declare function resolveScreenCredentials(machineId: string, options?: ScreenCredentialOptions): ScreenCredentialResolution;
17
41
  /**
18
42
  * Build the macOS command that opens Screen Sharing to a machine.
19
43
  * `open vnc://user@host` launches Screen Sharing.app pointed at the resolved route.
@@ -32,4 +56,11 @@ export declare function buildScreenCommand(machineId: string, options?: MachineR
32
56
  * password or runs under an already-root context).
33
57
  */
34
58
  export declare function buildScreenEnableRemoteCommand(user: string, vncPassword: string): string;
59
+ /**
60
+ * Build the remote root command used by secure screen-enable plans.
61
+ * The VNC password is read from stdin so it is not embedded in shell history,
62
+ * generated command text, or the SSH remote command arguments.
63
+ */
64
+ export declare function buildScreenEnableRemoteCommandFromStdin(user: string): string;
65
+ export declare function buildScreenEnableCommand(machineId: string, options?: ScreenEnableCommandOptions): ScreenEnableCommandPlan;
35
66
  //# sourceMappingURL=screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/commands/screen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE5J,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE,sBAAsB,CAAC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAgBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,oBAAoB,CA+B9G;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAG/F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAcxF"}
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/commands/screen.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC5B,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,+BAA+B,uCAAuC,CAAC;AAEpF,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE,sBAAsB,CAAC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;CACzD;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAwB,SAAQ,mBAAmB;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA2B,SAAQ,uBAAuB;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AA6BD,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,oBAAoB,CA+B9G;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,0BAA0B,CAwB7H;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAG/F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAcxF;AAED;;;;GAIG;AACH,wBAAgB,uCAAuC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAe5E;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,uBAAuB,CAc7H"}
package/dist/index.js CHANGED
@@ -13859,15 +13859,32 @@ function runSetup(machineId, options = {}) {
13859
13859
  return summary;
13860
13860
  }
13861
13861
  // src/commands/screen.ts
13862
+ var DEFAULT_SCREEN_SECRET_NAMESPACE = "hasna/xyz/opensource/machines/prod";
13862
13863
  function shellQuote7(value) {
13863
13864
  return `'${value.replace(/'/g, "'\\''")}'`;
13864
13865
  }
13866
+ function shellCommand2(command) {
13867
+ return command.map(shellQuote7).join(" ");
13868
+ }
13869
+ function metadataString2(metadata, keys) {
13870
+ if (!metadata)
13871
+ return null;
13872
+ for (const key of keys) {
13873
+ const value = metadata[key];
13874
+ if (typeof value === "string" && value.trim())
13875
+ return value.trim();
13876
+ }
13877
+ return null;
13878
+ }
13865
13879
  function splitTarget(target) {
13866
13880
  const at = target.indexOf("@");
13867
13881
  if (at === -1)
13868
13882
  return [null, target];
13869
13883
  return [target.slice(0, at), target.slice(at + 1)];
13870
13884
  }
13885
+ function defaultScreenPasswordSecretKey(machineId) {
13886
+ return `${DEFAULT_SCREEN_SECRET_NAMESPACE}/screen-${machineId}-vnc-password`;
13887
+ }
13871
13888
  function resolveScreenTarget(machineId, options = {}) {
13872
13889
  const resolved = resolveMachineRoute(machineId, options);
13873
13890
  if (!resolved.ok || !resolved.target) {
@@ -13893,6 +13910,30 @@ function resolveScreenTarget(machineId, options = {}) {
13893
13910
  warnings: resolved.warnings
13894
13911
  };
13895
13912
  }
13913
+ function resolveScreenCredentials(machineId, options = {}) {
13914
+ const topology = options.topology ?? discoverMachineTopology(options);
13915
+ const screen = resolveScreenTarget(machineId, { ...options, topology });
13916
+ const entry = topology.machines.find((machine) => machine.machine_id === screen.machineId);
13917
+ const metadata = entry?.metadata;
13918
+ const metadataUser = metadataString2(metadata, ["screenUser", "screen_user", "user", "username"]);
13919
+ const metadataPasswordSecret = metadataString2(metadata, [
13920
+ "screenPasswordSecret",
13921
+ "screen_password_secret",
13922
+ "screenVncPasswordSecret",
13923
+ "screen_vnc_password_secret",
13924
+ "vncPasswordSecret",
13925
+ "vnc_password_secret"
13926
+ ]);
13927
+ const user = options.user ?? screen.user ?? metadataUser;
13928
+ const passwordSecretKey = options.passwordSecretKey ?? metadataPasswordSecret ?? defaultScreenPasswordSecretKey(screen.machineId);
13929
+ return {
13930
+ machineId: screen.machineId,
13931
+ user: user ?? null,
13932
+ userSource: options.user ? "option" : screen.user ? "route" : metadataUser ? "metadata" : "missing",
13933
+ passwordSecretKey,
13934
+ passwordSecretSource: options.passwordSecretKey ? "option" : metadataPasswordSecret ? "metadata" : "default"
13935
+ };
13936
+ }
13896
13937
  function buildScreenCommand(machineId, options = {}) {
13897
13938
  const resolved = resolveScreenTarget(machineId, options);
13898
13939
  return `open ${resolved.url}`;
@@ -13907,6 +13948,37 @@ function buildScreenEnableRemoteCommand(user, vncPassword) {
13907
13948
  ];
13908
13949
  return lines.join(" && ");
13909
13950
  }
13951
+ function buildScreenEnableRemoteCommandFromStdin(user) {
13952
+ const kickstart = "/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart";
13953
+ const script = [
13954
+ "set -euo pipefail",
13955
+ 'user="$1"',
13956
+ "IFS= read -r vnc_pw",
13957
+ 'if [ -z "$vnc_pw" ]; then echo "missing VNC password on stdin" >&2; exit 1; fi',
13958
+ `kickstart=${shellQuote7(kickstart)}`,
13959
+ 'dseditgroup -o edit -a "$user" -t user com.apple.access_screensharing 2>/dev/null || true',
13960
+ "defaults write /Library/Preferences/com.apple.RemoteManagement AllowSRPForNetworkNodes -bool true",
13961
+ '"$kickstart" -configure -clientopts -setvnclegacy -vnclegacy yes -setvncpw -vncpw "$vnc_pw"',
13962
+ '"$kickstart" -activate -configure -access -on -users "$user" -privs -all -restart -agent -menu'
13963
+ ].join(`
13964
+ `);
13965
+ return `sudo -n -p '' /bin/bash -c ${shellQuote7(script)} -- ${shellQuote7(user)}`;
13966
+ }
13967
+ function buildScreenEnableCommand(machineId, options = {}) {
13968
+ const credentials = resolveScreenCredentials(machineId, options);
13969
+ if (!credentials.user) {
13970
+ throw new Error(`No screen-sharing user known for ${machineId}; pass --user <name> or set metadata.user in the manifest.`);
13971
+ }
13972
+ const secretsCommand = options.secretsCommand || "secrets";
13973
+ const remoteCommand = buildScreenEnableRemoteCommandFromStdin(credentials.user);
13974
+ return {
13975
+ machineId: credentials.machineId,
13976
+ user: credentials.user,
13977
+ passwordSecretKey: credentials.passwordSecretKey,
13978
+ remoteCommand,
13979
+ command: `${shellCommand2([secretsCommand, "get", credentials.passwordSecretKey])} | ${buildSshCommand(machineId, remoteCommand, options)}`
13980
+ };
13981
+ }
13910
13982
  // src/commands/sync.ts
13911
13983
  import { existsSync as existsSync7, lstatSync, readFileSync as readFileSync5, symlinkSync, copyFileSync } from "fs";
13912
13984
  import { homedir as homedir5 } from "os";
@@ -23362,6 +23434,7 @@ export {
23362
23434
  resolveTables,
23363
23435
  resolveSshTarget,
23364
23436
  resolveScreenTarget,
23437
+ resolveScreenCredentials,
23365
23438
  resolveMachineWorkspace,
23366
23439
  resolveMachineRoute,
23367
23440
  resolveBackupTarget,
@@ -23425,6 +23498,7 @@ export {
23425
23498
  diffClaudeCli,
23426
23499
  diffApps,
23427
23500
  detectCurrentMachineManifest,
23501
+ defaultScreenPasswordSecretKey,
23428
23502
  createMcpServer,
23429
23503
  createMachineResolverSnapshot,
23430
23504
  countRuns,
@@ -23435,7 +23509,9 @@ export {
23435
23509
  buildSshCommand,
23436
23510
  buildSetupPlan,
23437
23511
  buildServer,
23512
+ buildScreenEnableRemoteCommandFromStdin,
23438
23513
  buildScreenEnableRemoteCommand,
23514
+ buildScreenEnableCommand,
23439
23515
  buildScreenCommand,
23440
23516
  buildClaudeInstallPlan,
23441
23517
  buildCertPlan,
@@ -23465,6 +23541,7 @@ export {
23465
23541
  MACHINES_BACKUP_PREFIX_ENV,
23466
23542
  MACHINES_BACKUP_BUCKET_FALLBACK_ENV,
23467
23543
  MACHINES_BACKUP_BUCKET_ENV,
23544
+ DEFAULT_SCREEN_SECRET_NAMESPACE,
23468
23545
  DEFAULT_MACHINE_RESOLVER_TTL_MS,
23469
23546
  DEFAULT_BACKUP_PREFIX,
23470
23547
  CROSSREFS_KEY
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/machines",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "description": "Machine fleet management CLI + MCP for developers",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",