@mu-cabin/muc-cli 0.5.2 → 0.5.4

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/muc.mjs CHANGED
@@ -21479,7 +21479,7 @@ function file() {
21479
21479
  return join2(dir(), "config.json");
21480
21480
  }
21481
21481
  var CONFIG_SCHEMA_VERSION = 1;
21482
- var CHAT_OS_VALUES = ["Windows", "OS X", "Mac", "iOS", "Android"];
21482
+ var CHAT_OS_VALUES = ["Windows", "OS X", "Mac", "iOS", "Android", "iPad"];
21483
21483
  var MucConfigSchema = external_exports.object({
21484
21484
  appKey: external_exports.string().regex(/^[0-9a-f]{8}$/, "appKey must be 8 lowercase hex chars"),
21485
21485
  appId: external_exports.string().regex(/^[0-9a-f]{24}$/, "appId must be 24 lowercase hex chars"),
@@ -21499,9 +21499,9 @@ var MucConfigSchema = external_exports.object({
21499
21499
  // any other → that literal string (e.g. "\n— bot")
21500
21500
  // Per-call `--tail` / `--no-tail` flags on `muc send` override this.
21501
21501
  messageTail: external_exports.string().max(200).optional(),
21502
- // Default chat-channel `os` slot for TCP commands when --os is not passed.
21503
- // PC ({Windows, OS X, Mac}) vs Mobile ({iOS, Android}); same slot as the
21504
- // running desktop will kick it. Built-in fallback is 'iOS' (Mobile).
21502
+ // Default logical chat slot for TCP commands when --os is not passed.
21503
+ // PC: Windows/OS X/Mac; Mobile: iOS/Android; iPad: iPad. The iPad value is
21504
+ // serialized on the wire as os=iOS + browser=iPad. Built-in fallback: iPad.
21505
21505
  defaultChatOs: external_exports.enum(CHAT_OS_VALUES).optional(),
21506
21506
  // Opt-in: when true, the assistant daemon writes a live status headline
21507
21507
  // to your profile signature (`ext.sign`, visible to anyone who hovers
@@ -21652,7 +21652,7 @@ var PLATFORM_WIN = 4;
21652
21652
  var PLATFORM_MAC = 5;
21653
21653
  var SECURE_SYS_KEY = "mx_app_key";
21654
21654
  var PWD_DES_IV = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]);
21655
- var DEFAULT_CHAT_OS = "iOS";
21655
+ var DEFAULT_CHAT_OS = "iPad";
21656
21656
  var DEFAULT_REST_PLATFORM = PLATFORM_IOS;
21657
21657
  function defaultMessageTail(userName) {
21658
21658
  const name = userName.trim() || "unknown";
@@ -21855,7 +21855,8 @@ var CHAT_OS_MAP = {
21855
21855
  mac: "Mac",
21856
21856
  macos: "Mac",
21857
21857
  ios: "iOS",
21858
- android: "Android"
21858
+ android: "Android",
21859
+ ipad: "iPad"
21859
21860
  };
21860
21861
  function parseChatOs(raw) {
21861
21862
  const k = raw.trim().toLowerCase();
@@ -21863,7 +21864,7 @@ function parseChatOs(raw) {
21863
21864
  throw new CliError(
21864
21865
  "USAGE",
21865
21866
  `invalid --os: ${raw}`,
21866
- "expected windows|osx|mac|ios|android"
21867
+ "expected windows|osx|mac|ios|android|ipad"
21867
21868
  );
21868
21869
  }
21869
21870
  function resolveChatOs(optsOs, cfg) {
@@ -22494,7 +22495,7 @@ function register(program3, variant) {
22494
22495
  const isAssistant = variant?.kind === "assistant";
22495
22496
  define2(program3, {
22496
22497
  name: isAssistant ? "assistant-login" : "login",
22497
- description: isAssistant ? "Authenticate the assistant daemon's DEDICATED account and persist its tokens to ~/.muc/assistant/credentials.json (separate from your personal ~/.muc/credentials.json \u2014 the two never rotate each other's tokenPwd). Requires `muc assistant-init` first. Defaults --platform to mac (the daemon parks on the PC chat slot via cfg.defaultChatOs). Run interactively: `muc assistant-login --account <UID>` prompts for the password with echo off." : "Authenticate against MUC and persist accessToken + tokenPwd. Run interactively in your own terminal: `muc login --account <UID>` prompts for the password with echo off. `--token-pwd` reuses the saved refresh token and needs no password (this is the only form an agent should run silently). Avoid `--password` / `--password-stdin` / `$MUC_PASSWORD` \u2014 they leak the password into shell history, process lists, and agent chat scrollback.",
22498
+ description: isAssistant ? "Authenticate the assistant daemon's DEDICATED account and persist its tokens to ~/.muc/assistant/credentials.json (separate from your personal ~/.muc/credentials.json \u2014 the two never rotate each other's tokenPwd). Requires `muc assistant-init` first. REST login still defaults --platform to mac; the daemon chat connection separately defaults to the iPad slot via cfg.defaultChatOs. Run interactively: `muc assistant-login --account <UID>` prompts for the password with echo off." : "Authenticate against MUC and persist accessToken + tokenPwd. Run interactively in your own terminal: `muc login --account <UID>` prompts for the password with echo off. `--token-pwd` reuses the saved refresh token and needs no password (this is the only form an agent should run silently). Avoid `--password` / `--password-stdin` / `$MUC_PASSWORD` \u2014 they leak the password into shell history, process lists, and agent chat scrollback.",
22498
22499
  output: Output,
22499
22500
  configure: (cmd) => {
22500
22501
  cmd.option("-a, --account <uid>", "MUC account uid (uppercase, e.g. WANGLULU8)").option("-p, --password <pwd>", "plaintext password (avoid; ends up in shell history)").option("--password-stdin", "read password from stdin (no trailing newline)").option("--token-pwd", "reuse the saved tokenPwd (skips password)").option("--platform <p>", "ios|android|win|mac or numeric code", (v) => parsePlatform(v)).option("--app-version <v>", "override appVersion sent in the login form").option("--remember", "persist an encrypted copy of the password for unattended re-login").option("--no-remember", "wipe any previously --remember'ed password");
@@ -22714,7 +22715,7 @@ var FingerprintShape = external_exports.object({
22714
22715
  messageTail: external_exports.string(),
22715
22716
  // summary: "default" / "none" / "custom (N chars)"
22716
22717
  defaultChatOs: external_exports.string(),
22717
- // summary: explicit value or "default (iOS)"
22718
+ // summary: explicit value or "default (iPad)"
22718
22719
  exposeStatusInSignature: external_exports.boolean(),
22719
22720
  noRouteHint: external_exports.boolean(),
22720
22721
  allowAssistantSharedProfile: external_exports.boolean(),
@@ -22911,10 +22912,10 @@ function register2(program3, variant) {
22911
22912
  const isAssistant = variant?.kind === "assistant";
22912
22913
  define2(program3, {
22913
22914
  name: isAssistant ? "assistant-init" : "init",
22914
- description: isAssistant ? "Bootstrap or update the assistant daemon's DEDICATED-account tenant config at ~/.muc/assistant/config.json (separate from your personal ~/.muc/config.json). Provisioning this makes `muc assistantd-start` authenticate as that account on its own chat slot (defaults defaultChatOs=Windows \u2192 PC slot, so it coexists with a real iPhone on the Mobile slot). Tenant secrets default to whatever your personal `muc init` saved \u2014 pass --app-key/--app-id/--sign-secret-* to override. Then run `muc assistant-login`." : "Bootstrap or update ~/.muc/config.json (tenant config: APP_KEY, APP_ID, signing secrets, hosts/ports). Interactive prompts by default; non-interactive via flags or MUC_* env vars. WARNING: env vars are visible to other processes via /proc/<pid>/environ and shell history \u2014 prefer interactive or flag-based for production.",
22915
+ description: isAssistant ? "Bootstrap or update the assistant daemon's DEDICATED-account tenant config at ~/.muc/assistant/config.json (separate from your personal ~/.muc/config.json). Provisioning this makes `muc assistantd-start` authenticate as that account on its own chat slot (defaults defaultChatOs=iPad, separate from PC and Mobile; a real iPad on the same account may be kicked). Tenant secrets default to whatever your personal `muc init` saved \u2014 pass --app-key/--app-id/--sign-secret-* to override. Then run `muc assistant-login`." : "Bootstrap or update ~/.muc/config.json (tenant config: APP_KEY, APP_ID, signing secrets, hosts/ports). Interactive prompts by default; non-interactive via flags or MUC_* env vars. WARNING: env vars are visible to other processes via /proc/<pid>/environ and shell history \u2014 prefer interactive or flag-based for production.",
22915
22916
  output: Output2,
22916
22917
  configure: (cmd) => {
22917
- cmd.option("--check", "verify config presence and validity (exit 0 ok / 6 missing / 7 invalid); does not modify").option("--show", "print the saved config (secrets are always redacted in both human and JSON output; cat ~/.muc/config.json for raw values)").option("--reset", "delete ~/.muc/config.json (requires --yes)").option("--yes", "confirm a destructive action like --reset").option("--no-interactive", "fail rather than prompt for missing fields").option("--app-key <hex>", "tenant APP_KEY (8 lowercase hex)").option("--app-id <hex>", "tenant APP_ID (24 lowercase hex)").option("--sign-secret-rest <s>", "REST signing secret").option("--sign-secret-secure <s>", "secure-API signing secret").option("--pwd-des-key <s>", "password DES key (defaults to APP_KEY)").option("--rest-host <url>", "REST host URL").option("--sso-host <url>", "SSO host URL (optional)").option("--im-host <h>", "IM TCP host").option("--im-port <n>", "IM TCP port").option("--file-host <h>", "file-channel TCP host").option("--file-port <n>", "file-channel TCP port").option("--message-tail <text>", "custom signature appended to outbound `muc send` text (max 200 chars)").option("--no-message-tail", "explicitly disable the message tail (no signature appended)").option("--default-os <os>", "default chat-channel os slot for TCP commands when --os is not passed (windows|osx|mac|ios|android); built-in fallback is iOS").option("--expose-status-in-signature", "opt in: assistant daemon writes a live status headline to your MUC profile signature (visible to anyone who hovers your name). Daemon reads this flag at start; restart `muc assistantd-start` after toggling for the change to take effect.").option("--no-expose-status-in-signature", "opt out (default) of profile-signature status updates; /status chat command stays available regardless. Daemon reads this flag at start; restart needed for the change to take effect.").option("--allow-assistant-shared-profile", "advanced opt-in: allow assistantd to use this personal profile when no dedicated assistant profile exists").option("--no-allow-assistant-shared-profile", "opt out (default): assistantd must use its dedicated ~/.muc/assistant profile");
22918
+ cmd.option("--check", "verify config presence and validity (exit 0 ok / 6 missing / 7 invalid); does not modify").option("--show", "print the saved config (secrets are always redacted in both human and JSON output; cat ~/.muc/config.json for raw values)").option("--reset", "delete ~/.muc/config.json (requires --yes)").option("--yes", "confirm a destructive action like --reset").option("--no-interactive", "fail rather than prompt for missing fields").option("--app-key <hex>", "tenant APP_KEY (8 lowercase hex)").option("--app-id <hex>", "tenant APP_ID (24 lowercase hex)").option("--sign-secret-rest <s>", "REST signing secret").option("--sign-secret-secure <s>", "secure-API signing secret").option("--pwd-des-key <s>", "password DES key (defaults to APP_KEY)").option("--rest-host <url>", "REST host URL").option("--sso-host <url>", "SSO host URL (optional)").option("--im-host <h>", "IM TCP host").option("--im-port <n>", "IM TCP port").option("--file-host <h>", "file-channel TCP host").option("--file-port <n>", "file-channel TCP port").option("--message-tail <text>", "custom signature appended to outbound `muc send` text (max 200 chars)").option("--no-message-tail", "explicitly disable the message tail (no signature appended)").option("--default-os <os>", "default chat-channel slot for TCP commands when --os is not passed (windows|osx|mac|ios|android|ipad); built-in fallback is iPad").option("--expose-status-in-signature", "opt in: assistant daemon writes a live status headline to your MUC profile signature (visible to anyone who hovers your name). Daemon reads this flag at start; restart `muc assistantd-start` after toggling for the change to take effect.").option("--no-expose-status-in-signature", "opt out (default) of profile-signature status updates; /status chat command stays available regardless. Daemon reads this flag at start; restart needed for the change to take effect.").option("--allow-assistant-shared-profile", "advanced opt-in: allow assistantd to use this personal profile when no dedicated assistant profile exists").option("--no-allow-assistant-shared-profile", "opt out (default): assistantd must use its dedicated ~/.muc/assistant profile");
22918
22919
  },
22919
22920
  run: async (cmd) => {
22920
22921
  const opts = cmd.opts();
@@ -23056,7 +23057,7 @@ function register2(program3, variant) {
23056
23057
  }
23057
23058
  }
23058
23059
  }
23059
- const chatOsDefault = isAssistant ? "windows" : DEFAULT_CHAT_OS.toLowerCase();
23060
+ const chatOsDefault = DEFAULT_CHAT_OS.toLowerCase();
23060
23061
  let defaultChatOs;
23061
23062
  let dcoRaw;
23062
23063
  const dcoFlag = opts.defaultOs;
@@ -23069,7 +23070,7 @@ function register2(program3, variant) {
23069
23070
  defaultChatOs = existing.defaultChatOs;
23070
23071
  } else if (interactive) {
23071
23072
  const answer = await readLine(
23072
- "Default chat OS slot (windows|osx|mac|ios|android)",
23073
+ "Default chat slot (windows|osx|mac|ios|android|ipad)",
23073
23074
  chatOsDefault
23074
23075
  );
23075
23076
  if (answer.length > 0) dcoRaw = answer;
@@ -23078,7 +23079,7 @@ function register2(program3, variant) {
23078
23079
  defaultChatOs = parseChatOs(dcoRaw);
23079
23080
  }
23080
23081
  if (defaultChatOs === void 0 && isAssistant) {
23081
- defaultChatOs = "Windows";
23082
+ defaultChatOs = DEFAULT_CHAT_OS;
23082
23083
  }
23083
23084
  const noRouteHint = existing?.noRouteHint;
23084
23085
  const sharedConflict = detectFlagConflict(process.argv.slice(2), "allow-assistant-shared-profile");
@@ -23913,12 +23914,15 @@ var MucChatSocket = class extends EventEmitter {
23913
23914
 
23914
23915
  // src/lib/chat.ts
23915
23916
  init_profile();
23917
+ function chatLoginIdentity(chatOs) {
23918
+ return chatOs === "iPad" ? { os: "iOS", browser: "iPad" } : { os: chatOs };
23919
+ }
23916
23920
  function buildLoginPayload(cred, chatOs, cfg) {
23917
23921
  return {
23918
23922
  login: {
23919
23923
  account: cred.uid,
23920
23924
  accessToken: cred.accessToken,
23921
- os: chatOs,
23925
+ ...chatLoginIdentity(chatOs),
23922
23926
  osVersion: release3(),
23923
23927
  sdkVersion: 21,
23924
23928
  deviceId: cred.deviceId,
@@ -25080,7 +25084,7 @@ function register10(program3) {
25080
25084
  description: 'Search groups the current user belongs to (TCP chat channel sid="team", cid="search_own_teams"). Pass --enrich for names + member counts. Will kick a session in the same online-slot.',
25081
25085
  output: Output10,
25082
25086
  configure: (cmd) => {
25083
- cmd.argument("<keyword>", "team name fragment").option("--enrich", "follow up with team/get_team per id").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
25087
+ cmd.argument("<keyword>", "team name fragment").option("--enrich", "follow up with team/get_team per id").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
25084
25088
  },
25085
25089
  run: async (cmd) => {
25086
25090
  const [kw] = cmd.args;
@@ -25205,7 +25209,7 @@ function register11(program3) {
25205
25209
  description: "List members of a joined group by TEAM_ID via TCP team/get_team_members. Pass --profiles to enrich department/title via organization search. Will kick a session in the same online-slot.",
25206
25210
  output: Output11,
25207
25211
  configure: (cmd) => {
25208
- cmd.argument("<teamId>", "team/group id from search-team").option("--profiles", "enrich each member with org directory fields via REST organization/search").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
25212
+ cmd.argument("<teamId>", "team/group id from search-team").option("--profiles", "enrich each member with org directory fields via REST organization/search").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
25209
25213
  },
25210
25214
  run: async (cmd) => {
25211
25215
  const [id] = cmd.args;
@@ -25500,7 +25504,7 @@ function register15(program3) {
25500
25504
  description: "After chat-login, run sync/syncDone, sync/syncSession, sync/syncWithdraws and return the raw responses.",
25501
25505
  output: Output15,
25502
25506
  configure: (cmd) => {
25503
- cmd.option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
25507
+ cmd.option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
25504
25508
  },
25505
25509
  run: async (cmd) => {
25506
25510
  const { os: optsOs, debug, daemon } = cmd.opts();
@@ -25825,7 +25829,7 @@ function register17(program3) {
25825
25829
  description: "Send a plain-text chat message via the TCP chat channel. VISIBLE TO THE RECIPIENT \u2014 non-self targets require --yes-not-self.",
25826
25830
  output: Output17,
25827
25831
  configure: (cmd) => {
25828
- cmd.argument("<text...>", "message text (variadic; words rejoined with spaces)").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--tail <text>", "override message tail for this call only").option("--no-tail", "do not append a message tail for this call").option(
25832
+ cmd.argument("<text...>", "message text (variadic; words rejoined with spaces)").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--tail <text>", "override message tail for this call only").option("--no-tail", "do not append a message tail for this call").option(
25829
25833
  "--at <uid[:name][,uid[:name]...]>",
25830
25834
  '@-mention one or more uids in a group (use "uid:name" to control the rendered display name; group target only)'
25831
25835
  ).option("--debug", "forward wire-level debug events to stderr");
@@ -26689,7 +26693,7 @@ function register18(program3) {
26689
26693
  description: "Upload a local file (TCP 6101) and broadcast as MESSAGE_CHAT_FILE (subType=7). VISIBLE \u2014 non-self targets require --yes-not-self.",
26690
26694
  output: Output18,
26691
26695
  configure: (cmd) => {
26692
- cmd.argument("<file>", "absolute or relative path to a local file").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "resolve target + sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel os slot for the talk/sendMsg (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
26696
+ cmd.argument("<file>", "absolute or relative path to a local file").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default if no target given)").option("--yes-not-self", "required to confirm sending to anyone other than yourself").option("--dry-run", "resolve target + sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel slot for talk/sendMsg (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
26693
26697
  },
26694
26698
  run: async (cmd) => {
26695
26699
  const filePath = path2.resolve(String(cmd.args[0] ?? ""));
@@ -26887,7 +26891,7 @@ function register19(program3) {
26887
26891
  description: "Upload an image (TCP 6101) and broadcast as MESSAGE_CHAT_IMAGE (subType=8). VISIBLE \u2014 non-self targets require --yes-not-self.",
26888
26892
  output: Output19,
26889
26893
  configure: (cmd) => {
26890
- cmd.argument("<file>", "PNG/JPEG/GIF/BMP image path").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--width <px>", "override probed image width", parsePosInt("--width")).option("--height <px>", "override probed image height", parsePosInt("--height")).option("--dry-run", "sanity-check the local file (probe + stat) but do not upload or send").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
26894
+ cmd.argument("<file>", "PNG/JPEG/GIF/BMP image path").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--width <px>", "override probed image width", parsePosInt("--width")).option("--height <px>", "override probed image height", parsePosInt("--height")).option("--dry-run", "sanity-check the local file (probe + stat) but do not upload or send").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
26891
26895
  },
26892
26896
  run: async (cmd) => {
26893
26897
  const filePath = path3.resolve(String(cmd.args[0] ?? ""));
@@ -27065,7 +27069,7 @@ function register20(program3) {
27065
27069
  description: "Upload an audio clip (TCP 6101) and broadcast as MESSAGE_CHAT_AUDIO (subType=9). VISIBLE \u2014 non-self targets require --yes-not-self.",
27066
27070
  output: Output20,
27067
27071
  configure: (cmd) => {
27068
- cmd.argument("<file>", "audio file path (typically .amr)").requiredOption("--duration <ms>", "clip duration in milliseconds", parsePosInt("--duration")).option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--dry-run", "sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27072
+ cmd.argument("<file>", "audio file path (typically .amr)").requiredOption("--duration <ms>", "clip duration in milliseconds", parsePosInt("--duration")).option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "send to your own uid (default)").option("--yes-not-self", "required for non-self targets").option("--dry-run", "sanity-check the local file but do not upload or send").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27069
27073
  },
27070
27074
  run: async (cmd) => {
27071
27075
  const filePath = path4.resolve(String(cmd.args[0] ?? ""));
@@ -27326,7 +27330,7 @@ function register22(program3) {
27326
27330
  description: "Withdraw a previously-sent message (P2P_MSG_CANCEL). VISIBLE TO THE RECIPIENT \u2014 non-self targets require --yes-not-self.",
27327
27331
  output: Output22,
27328
27332
  configure: (cmd) => {
27329
- cmd.requiredOption("--mid <mid>", "message id to withdraw").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "withdraw in your self-chat (default if no target)").option("--yes-not-self", "required to confirm withdrawing in a non-self conversation").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27333
+ cmd.requiredOption("--mid <mid>", "message id to withdraw").option("--peer <uid>", "1:1 chat with a peer uid").option("--team <id>", "group chat with a team id").option("--to-self", "withdraw in your self-chat (default if no target)").option("--yes-not-self", "required to confirm withdrawing in a non-self conversation").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27330
27334
  },
27331
27335
  run: async (cmd) => {
27332
27336
  const opts = cmd.opts();
@@ -27440,7 +27444,7 @@ function register23(program3) {
27440
27444
  description: "Send user/delSession to drop the local session record on all of the user's devices.",
27441
27445
  output: Output23,
27442
27446
  configure: (cmd) => {
27443
- cmd.option("--peer <uid>", "session with a peer uid").option("--team <id>", "session with a team id").option("--to-self", "self-chat session (default if no target)").option("--yes-not-self", "required to confirm clearing a non-self session").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27447
+ cmd.option("--peer <uid>", "session with a peer uid").option("--team <id>", "session with a team id").option("--to-self", "self-chat session (default if no target)").option("--yes-not-self", "required to confirm clearing a non-self session").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and use a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27444
27448
  },
27445
27449
  run: async (cmd) => {
27446
27450
  const opts = cmd.opts();
@@ -27506,6 +27510,254 @@ function register23(program3) {
27506
27510
  });
27507
27511
  }
27508
27512
 
27513
+ // src/lib/sticky.ts
27514
+ var STICKY_CACHE_KEY = "sticky_on_top_id";
27515
+ function stickyCacheFrom() {
27516
+ return process.platform === "win32" ? "WIN" : "MAC";
27517
+ }
27518
+ var STICKY_LIST_PROBE_SID = "__muc_cli_sticky_list_probe__";
27519
+ function buildStickyCacheFieldData(args) {
27520
+ return {
27521
+ from: args.from ?? stickyCacheFrom(),
27522
+ key: [STICKY_CACHE_KEY],
27523
+ subkey: null,
27524
+ value: [args.sId],
27525
+ op: args.op
27526
+ };
27527
+ }
27528
+ function extractStickyIds(frame) {
27529
+ if (!frame || typeof frame !== "object") return [];
27530
+ const f = frame;
27531
+ if (f.err) return [];
27532
+ const candidates = [f.respond, f.data, f];
27533
+ for (const c of candidates) {
27534
+ if (!c || typeof c !== "object" || Array.isArray(c)) continue;
27535
+ const ids = c.sticky_on_top_id;
27536
+ if (Array.isArray(ids)) {
27537
+ return ids.map((x) => String(x)).filter((s) => s && s !== STICKY_LIST_PROBE_SID);
27538
+ }
27539
+ }
27540
+ return [];
27541
+ }
27542
+ function classifySid(sId, selfUid) {
27543
+ if (!sId) return { kind: "unknown" };
27544
+ if (sId.includes("|")) {
27545
+ const parts = sId.split("|");
27546
+ if (parts.length === 2) {
27547
+ const self2 = selfUid.toUpperCase();
27548
+ const a = parts[0].toUpperCase();
27549
+ const b = parts[1].toUpperCase();
27550
+ if (a === self2 && b === self2) return { kind: "self" };
27551
+ const peer = a === self2 ? parts[1] : b === self2 ? parts[0] : parts[1];
27552
+ return { kind: "p2p", peer };
27553
+ }
27554
+ }
27555
+ if (/^\d{10,}$/.test(sId)) return { kind: "team", team_id: sId };
27556
+ return { kind: "unknown" };
27557
+ }
27558
+ function stickySessionRows(ids, selfUid) {
27559
+ return ids.map((sId) => ({ sId, ...classifySid(sId, selfUid) }));
27560
+ }
27561
+
27562
+ // src/commands/pin.ts
27563
+ var SessionRow = external_exports.object({
27564
+ sId: external_exports.string(),
27565
+ kind: external_exports.enum(["team", "p2p", "self", "unknown"]),
27566
+ peer: external_exports.string().optional(),
27567
+ team_id: external_exports.string().optional()
27568
+ });
27569
+ var MutateOutput = external_exports.object({
27570
+ action: external_exports.enum(["pin", "unpin"]),
27571
+ scene: external_exports.enum(["p2p", "team_g"]),
27572
+ toId: external_exports.string(),
27573
+ sId: external_exports.string(),
27574
+ dryRun: external_exports.boolean(),
27575
+ sticky_ids: external_exports.array(external_exports.string()),
27576
+ sessions: external_exports.array(SessionRow),
27577
+ frame: external_exports.unknown().optional(),
27578
+ raw: external_exports.unknown().optional(),
27579
+ route: external_exports.enum(["daemon", "direct"]).optional(),
27580
+ daemon_kind: external_exports.literal("personal").optional()
27581
+ });
27582
+ var ListOutput = external_exports.object({
27583
+ sticky_ids: external_exports.array(external_exports.string()),
27584
+ sessions: external_exports.array(SessionRow),
27585
+ count: external_exports.number(),
27586
+ dryRun: external_exports.boolean().optional(),
27587
+ frame: external_exports.unknown().optional(),
27588
+ raw: external_exports.unknown().optional(),
27589
+ route: external_exports.enum(["daemon", "direct"]).optional(),
27590
+ daemon_kind: external_exports.literal("personal").optional()
27591
+ });
27592
+ function resolveTarget(opts, selfUid, label) {
27593
+ requireSingleTarget(
27594
+ { peer: opts.peer, team: opts.team, sid: opts.sid, toSelf: opts.toSelf },
27595
+ label
27596
+ );
27597
+ if (opts.sid) {
27598
+ const sId2 = opts.sid;
27599
+ if (sId2.includes("|")) {
27600
+ return { scene: "p2p", toId: selfUid, sId: sId2 };
27601
+ }
27602
+ return { scene: "team_g", toId: sId2, sId: sId2 };
27603
+ }
27604
+ let scene;
27605
+ let toId;
27606
+ if (opts.team) {
27607
+ scene = "team_g";
27608
+ toId = opts.team;
27609
+ } else if (opts.toSelf || !opts.peer && !opts.team) {
27610
+ scene = "p2p";
27611
+ toId = selfUid;
27612
+ } else {
27613
+ scene = "p2p";
27614
+ toId = opts.peer.toUpperCase();
27615
+ }
27616
+ const sId = buildSId(scene, selfUid, toId);
27617
+ return { scene, toId, sId };
27618
+ }
27619
+ async function tcpUserSetCacheField(args) {
27620
+ const { data, debug, daemon, cmd, credUid } = args;
27621
+ let os = args.os;
27622
+ const daemonDisabled = daemonRoutingDisabled({ daemon });
27623
+ if (daemonDisabled) await assertNoAssistantSlotConflict(credUid);
27624
+ const daemonRoute = daemonDisabled ? null : await probeDaemonRoute(credUid);
27625
+ if (daemonRoute) {
27626
+ os = assertNoSlotConflict({
27627
+ route: daemonRoute,
27628
+ userOs: args.optsOs ?? os,
27629
+ userExplicit: userExplicitOs(cmd)
27630
+ });
27631
+ const res = await tcpSendViaDaemon({ sid: "user", cid: "setCacheField", data });
27632
+ return { res, route: "daemon", daemon_kind: "personal", os };
27633
+ }
27634
+ const cred = requireAuth(loadCredentials());
27635
+ const session = await openChatSession(cred, { os, debug });
27636
+ try {
27637
+ const res = await session.socket.send("user", "setCacheField", data);
27638
+ return { res, route: "direct", os };
27639
+ } finally {
27640
+ session.socket.close();
27641
+ }
27642
+ }
27643
+ function registerPin(program3) {
27644
+ define2(program3, {
27645
+ name: "pin",
27646
+ description: "Pin a chat session to the top of the conversation list (TCP user/setCacheField sticky_on_top_id, op=1). Multi-device via user cache. Will kick a session in the same online-slot.",
27647
+ output: MutateOutput,
27648
+ configure: (cmd) => {
27649
+ cmd.option("--peer <uid>", "1:1 session with this uid").option("--team <id>", "group session (team id)").option("--to-self", "self-chat session (default if no target)").option("--sid <sId>", "raw session id (teamId or UID|UID); overrides --peer/--team/--to-self").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27650
+ },
27651
+ run: async (cmd) => mutateSticky(cmd, "pin")
27652
+ });
27653
+ }
27654
+ function registerUnpin(program3) {
27655
+ define2(program3, {
27656
+ name: "unpin",
27657
+ description: "Remove a session from the pin list (TCP user/setCacheField sticky_on_top_id, op=2). Will kick a session in the same online-slot.",
27658
+ output: MutateOutput,
27659
+ configure: (cmd) => {
27660
+ cmd.option("--peer <uid>", "1:1 session with this uid").option("--team <id>", "group session (team id)").option("--to-self", "self-chat session (default if no target)").option("--sid <sId>", "raw session id (teamId or UID|UID); overrides --peer/--team/--to-self").option("--dry-run", "build the frame but do not connect").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27661
+ },
27662
+ run: async (cmd) => mutateSticky(cmd, "unpin")
27663
+ });
27664
+ }
27665
+ async function mutateSticky(cmd, action) {
27666
+ const opts = cmd.opts();
27667
+ const cfg = requireConfig();
27668
+ const cred = requireAuth(loadCredentials());
27669
+ const os = resolveChatOs(opts.os, cfg);
27670
+ const { scene, toId, sId } = resolveTarget(opts, cred.uid, action);
27671
+ const op = action === "pin" ? 1 : 2;
27672
+ const data = buildStickyCacheFieldData({ sId, op });
27673
+ const frame = { sid: "user", cid: "setCacheField", data };
27674
+ if (opts.dryRun) {
27675
+ return {
27676
+ action,
27677
+ scene,
27678
+ toId,
27679
+ sId,
27680
+ dryRun: true,
27681
+ sticky_ids: [],
27682
+ sessions: [],
27683
+ frame
27684
+ };
27685
+ }
27686
+ const { res, route: route2, daemon_kind } = await tcpUserSetCacheField({
27687
+ data,
27688
+ os,
27689
+ optsOs: opts.os,
27690
+ debug: opts.debug,
27691
+ daemon: opts.daemon,
27692
+ cmd,
27693
+ credUid: cred.uid
27694
+ });
27695
+ if (res?.err) {
27696
+ throw new CliError("REMOTE_ERROR", `${action} rejected: ${JSON.stringify(res).slice(0, 400)}`);
27697
+ }
27698
+ const sticky_ids = extractStickyIds(res);
27699
+ return {
27700
+ action,
27701
+ scene,
27702
+ toId,
27703
+ sId,
27704
+ dryRun: false,
27705
+ sticky_ids,
27706
+ sessions: stickySessionRows(sticky_ids, cred.uid),
27707
+ raw: res,
27708
+ route: route2,
27709
+ daemon_kind
27710
+ };
27711
+ }
27712
+ function registerPinned(program3) {
27713
+ define2(program3, {
27714
+ name: "pinned",
27715
+ description: "List pinned session sIds from user cache (TCP user/setCacheField no-op delete probe \u2192 sticky_on_top_id). Will kick a session in the same online-slot.",
27716
+ output: ListOutput,
27717
+ configure: (cmd) => {
27718
+ cmd.option("--dry-run", "build the probe frame but do not connect").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27719
+ },
27720
+ run: async (cmd) => {
27721
+ const opts = cmd.opts();
27722
+ const cfg = requireConfig();
27723
+ const cred = requireAuth(loadCredentials());
27724
+ const os = resolveChatOs(opts.os, cfg);
27725
+ const data = buildStickyCacheFieldData({ sId: STICKY_LIST_PROBE_SID, op: 2 });
27726
+ const frame = { sid: "user", cid: "setCacheField", data };
27727
+ if (opts.dryRun) {
27728
+ return { sticky_ids: [], sessions: [], count: 0, dryRun: true, frame };
27729
+ }
27730
+ const { res, route: route2, daemon_kind } = await tcpUserSetCacheField({
27731
+ data,
27732
+ os,
27733
+ optsOs: opts.os,
27734
+ debug: opts.debug,
27735
+ daemon: opts.daemon,
27736
+ cmd,
27737
+ credUid: cred.uid
27738
+ });
27739
+ if (res?.err) {
27740
+ throw new CliError("REMOTE_ERROR", `pinned list rejected: ${JSON.stringify(res).slice(0, 400)}`);
27741
+ }
27742
+ const sticky_ids = extractStickyIds(res);
27743
+ return {
27744
+ sticky_ids,
27745
+ sessions: stickySessionRows(sticky_ids, cred.uid),
27746
+ count: sticky_ids.length,
27747
+ dryRun: false,
27748
+ raw: res,
27749
+ route: route2,
27750
+ daemon_kind
27751
+ };
27752
+ }
27753
+ });
27754
+ }
27755
+ function register24(program3) {
27756
+ registerPin(program3);
27757
+ registerUnpin(program3);
27758
+ registerPinned(program3);
27759
+ }
27760
+
27509
27761
  // src/lib/reactions.ts
27510
27762
  async function saveReply(cfg, args) {
27511
27763
  const { ctx, stickerId, senderName, isFromMe, selfAppKey } = args;
@@ -27572,7 +27824,7 @@ var Output24 = external_exports.object({
27572
27824
  emoticonId: external_exports.string().optional(),
27573
27825
  raw: external_exports.unknown()
27574
27826
  });
27575
- function register24(program3) {
27827
+ function register25(program3) {
27576
27828
  define2(program3, {
27577
27829
  name: "react",
27578
27830
  description: "Add a default reaction emoji to a message (POST emoticon/reply/save). VISIBLE TO THE TARGET CHAT \u2014 non-self targets require --yes-not-self.",
@@ -27669,7 +27921,7 @@ var Output25 = external_exports.object({
27669
27921
  reactions: external_exports.array(external_exports.unknown()),
27670
27922
  raw: external_exports.unknown()
27671
27923
  });
27672
- function register25(program3) {
27924
+ function register26(program3) {
27673
27925
  define2(program3, {
27674
27926
  name: "reactions",
27675
27927
  description: "List default emoji reactions on a message (GET emoticon/reply/list?mid=\u2026).",
@@ -27712,7 +27964,7 @@ var Output26 = external_exports.object({
27712
27964
  emoticonId: external_exports.string(),
27713
27965
  raw: external_exports.unknown()
27714
27966
  });
27715
- function register26(program3) {
27967
+ function register27(program3) {
27716
27968
  define2(program3, {
27717
27969
  name: "unreact",
27718
27970
  description: "Revoke one of your default emoji reactions (POST emoticon/reply/revocation). VISIBLE TO THE TARGET CHAT \u2014 non-self targets require --yes-not-self.",
@@ -27785,13 +28037,13 @@ var Output27 = external_exports.object({
27785
28037
  route: external_exports.enum(["daemon", "direct"]).optional(),
27786
28038
  daemon_kind: external_exports.literal("personal").optional()
27787
28039
  });
27788
- function register27(program3) {
28040
+ function register28(program3) {
27789
28041
  define2(program3, {
27790
28042
  name: "wait-react",
27791
- description: "Block until someone adds a default emoji reaction (subType=300) to --mid. Consumes a TCP slot \u2014 defaults to --os iOS (Mobile slot).",
28043
+ description: "Block until someone adds a default emoji reaction (subType=300) to --mid. Consumes a TCP slot \u2014 defaults to the iPad slot.",
27792
28044
  output: Output27,
27793
28045
  configure: (cmd) => {
27794
- cmd.requiredOption("--mid <mid>", "message id to wait for a reaction on").option("--from-uid <uid>", "only consider reactions from this UID (case-insensitive match against fId of the type=3 notification)").option("--emoji <name>", "only consider this default reaction emoji name, e.g. [\u70B9\u8D5E] or \u70B9\u8D5E").option("--sticker-id <name>", "deprecated alias for --emoji").option("--timeout <seconds>", "give up after this many seconds (default 300)", parsePosInt("--timeout"), 300).option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and wait on a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
28046
+ cmd.requiredOption("--mid <mid>", "message id to wait for a reaction on").option("--from-uid <uid>", "only consider reactions from this UID (case-insensitive match against fId of the type=3 notification)").option("--emoji <name>", "only consider this default reaction emoji name, e.g. [\u70B9\u8D5E] or \u70B9\u8D5E").option("--sticker-id <name>", "deprecated alias for --emoji").option("--timeout <seconds>", "give up after this many seconds (default 300)", parsePosInt("--timeout"), 300).option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and wait on a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
27795
28047
  },
27796
28048
  run: async (cmd) => {
27797
28049
  const opts = cmd.opts();
@@ -27998,13 +28250,13 @@ var STREAM_SCHEMA = {
27998
28250
  }
27999
28251
  ]
28000
28252
  };
28001
- function register28(program3) {
28253
+ function register29(program3) {
28002
28254
  defineStream(program3, {
28003
28255
  name: "listen",
28004
28256
  description: "Tail incoming chat messages, filtered by sender / team / peer / keyword / regex / @-mention. Emit one line per match \u2014 NDJSON envelope under `--json`, human summary otherwise. Consumes a TCP slot.",
28005
28257
  streamSchema: STREAM_SCHEMA,
28006
28258
  configure: (cmd) => {
28007
- cmd.option("--from-uid <uid>", "only emit messages from this UID (repeat for OR; matched against fId, case-insensitive)", collect, []).option("--team <id>", "only emit messages in this team (repeat for OR)", collect, []).option("--peer <uid>", "only emit p2p messages with this peer UID (repeat for OR)", collect, []).option("--contains <text>", "only emit messages whose text contains this substring, case-insensitive (repeat for OR)", collect, []).option("--regex <pattern>", "only emit messages whose text matches this RE2-flavoured regex (repeat for OR)", collect, []).option("--type <n>", "only emit messages of this `type` field (default 1; repeat for OR)", collectInt("--type"), []).option("--subtype <n>", "only emit messages of this `subType` field (repeat for OR; default = any)", collectInt("--subtype"), []).option("--max <n>", "exit after emitting this many matching lines", parsePosInt("--max")).option("--at-me", "only emit group messages that @-mention you (mirrors the desktop SDK isAtMe: scene=team_g, fId\u2260self, atIds includes self uid). Implies excluding self.").option("--include-self", "do not filter out messages whose fId is your own UID").option("--include-reactions", "also emit type=3 sub=300/301 (reaction add/remove) frames").option("--include-offline", "also emit messages flushed via cid=offMsg on connect (default: only post-connect msg)").option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and listen on a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
28259
+ cmd.option("--from-uid <uid>", "only emit messages from this UID (repeat for OR; matched against fId, case-insensitive)", collect, []).option("--team <id>", "only emit messages in this team (repeat for OR)", collect, []).option("--peer <uid>", "only emit p2p messages with this peer UID (repeat for OR)", collect, []).option("--contains <text>", "only emit messages whose text contains this substring, case-insensitive (repeat for OR)", collect, []).option("--regex <pattern>", "only emit messages whose text matches this RE2-flavoured regex (repeat for OR)", collect, []).option("--type <n>", "only emit messages of this `type` field (default 1; repeat for OR)", collectInt("--type"), []).option("--subtype <n>", "only emit messages of this `subType` field (repeat for OR; default = any)", collectInt("--subtype"), []).option("--max <n>", "exit after emitting this many matching lines", parsePosInt("--max")).option("--at-me", "only emit group messages that @-mention you (mirrors the desktop SDK isAtMe: scene=team_g, fId\u2260self, atIds includes self uid). Implies excluding self.").option("--include-self", "do not filter out messages whose fId is your own UID").option("--include-reactions", "also emit type=3 sub=300/301 (reaction add/remove) frames").option("--include-offline", "also emit messages flushed via cid=offMsg on connect (default: only post-connect msg)").option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--no-daemon", "bypass any running personal daemon and listen on a direct connection on your own slot (also: MUC_NO_DAEMON=1)").option("--debug", "forward wire-level debug events to stderr");
28008
28260
  },
28009
28261
  run: async (cmd) => {
28010
28262
  const opts = cmd.opts();
@@ -28394,7 +28646,7 @@ var ChatBroker = class {
28394
28646
  );
28395
28647
  }
28396
28648
  this.cred = cred;
28397
- this.os = this.opts.os ?? this.cfg.defaultChatOs ?? "iOS";
28649
+ this.os = this.opts.os ?? this.cfg.defaultChatOs ?? DEFAULT_CHAT_OS;
28398
28650
  this.startedAt = Date.now();
28399
28651
  ensureRuntimeDir(this.paths);
28400
28652
  writePid(this.paths, process.pid);
@@ -28773,13 +29025,14 @@ var ALLOWED_TCP_CALLS = /* @__PURE__ */ new Set([
28773
29025
  "team/search_own_teams",
28774
29026
  "team/get_team",
28775
29027
  "team/get_team_members",
28776
- "user/delSession"
29028
+ "user/delSession",
29029
+ "user/setCacheField"
28777
29030
  ]);
28778
29031
  function isAllowedTcpCall(sid, cid) {
28779
29032
  return ALLOWED_TCP_CALLS.has(`${sid}/${cid}`);
28780
29033
  }
28781
29034
  function shouldAuditTcpCall(sid, cid) {
28782
- return sid === "talk" && cid === "sendMsg" || sid === "user" && cid === "delSession";
29035
+ return sid === "talk" && cid === "sendMsg" || sid === "user" && (cid === "delSession" || cid === "setCacheField");
28783
29036
  }
28784
29037
  function readState(paths2) {
28785
29038
  if (!existsSync10(paths2.state)) return null;
@@ -28965,13 +29218,13 @@ var STREAM_SCHEMA2 = {
28965
29218
  }
28966
29219
  ]
28967
29220
  };
28968
- function register29(program3) {
29221
+ function register30(program3) {
28969
29222
  define2(program3, {
28970
29223
  name: "daemon-start",
28971
29224
  description: "Start the personal-account daemon in the foreground. Holds the chat socket for ordinary commands and writes raw daemon events.",
28972
29225
  output: StartOutput,
28973
29226
  configure: (cmd) => {
28974
- cmd.option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--on-kicked <policy>", "when another device takes the slot: pause, immediate, or a delay such as 30s/5m/2h/1d (default: 3m)", parseKickedRecoveryPolicy).option("--debug", "forward wire-level debug events to stderr");
29227
+ cmd.option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--on-kicked <policy>", "when another device takes the slot: pause, immediate, or a delay such as 30s/5m/2h/1d (default: 3m)", parseKickedRecoveryPolicy).option("--debug", "forward wire-level debug events to stderr");
28975
29228
  },
28976
29229
  run: async (cmd) => {
28977
29230
  const opts = cmd.opts();
@@ -31040,7 +31293,7 @@ var KNOWN_CLAUDE_MODELS = /* @__PURE__ */ new Set([
31040
31293
  "claude-haiku-4-5",
31041
31294
  "claude-haiku-4-5-20251001"
31042
31295
  ]);
31043
- var ListOutput = external_exports.object({
31296
+ var ListOutput2 = external_exports.object({
31044
31297
  count: external_exports.number(),
31045
31298
  assistants: external_exports.array(external_exports.object({
31046
31299
  name: external_exports.string(),
@@ -31086,7 +31339,7 @@ var LogOutput = external_exports.object({
31086
31339
  since_ms: external_exports.number(),
31087
31340
  rows: external_exports.array(LogRowSchema)
31088
31341
  });
31089
- function register30(program3) {
31342
+ function register31(program3) {
31090
31343
  define2(program3, {
31091
31344
  name: "assistant-new",
31092
31345
  description: "Create a new assistant scaffold under ~/.muc/assistants/<name>/. Edit the system-prompt.md afterwards.",
@@ -31187,7 +31440,7 @@ function register30(program3) {
31187
31440
  define2(program3, {
31188
31441
  name: "assistant-list",
31189
31442
  description: "List registered assistants and which are currently attached to the daemon.",
31190
- output: ListOutput,
31443
+ output: ListOutput2,
31191
31444
  run: async () => {
31192
31445
  const all = listAssistants();
31193
31446
  const active = await fetchActiveSet();
@@ -33251,7 +33504,7 @@ return the JSON envelope and let the daemon send.
33251
33504
  Every TCP verb above rides the assistant daemon's existing socket \u2014 no
33252
33505
  slot taken, no device kicked. This exception applies only inside
33253
33506
  assistant backend turns. Ordinary Codex/MUC skills use personal daemon
33254
- routing (\`muc daemon-start\`) and must follow their own warn-once rules.
33507
+ routing (\`muc daemon-start\`) and must follow the non-blocking iPad notice rules.
33255
33508
 
33256
33509
  ## Attachments and quotes
33257
33510
 
@@ -33708,7 +33961,7 @@ var AssistantDaemon = class {
33708
33961
  if (!usingAssistantProfile) {
33709
33962
  await assertNoPersonalDaemonConflict(this.cred.uid);
33710
33963
  }
33711
- this.os = this.opts.os ?? this.cfg.defaultChatOs ?? "iOS";
33964
+ this.os = this.opts.os ?? this.cfg.defaultChatOs ?? DEFAULT_CHAT_OS;
33712
33965
  ensureRuntimeDir2();
33713
33966
  writePid2(process.pid);
33714
33967
  this.startedAt = Date.now();
@@ -35305,7 +35558,8 @@ var ALLOWED_TCP_CALLS2 = /* @__PURE__ */ new Set([
35305
35558
  "team/search_own_teams",
35306
35559
  "team/get_team",
35307
35560
  "team/get_team_members",
35308
- "user/delSession"
35561
+ "user/delSession",
35562
+ "user/setCacheField"
35309
35563
  ]);
35310
35564
  function isAllowedTcpCall2(sid, cid) {
35311
35565
  return ALLOWED_TCP_CALLS2.has(`${sid}/${cid}`);
@@ -35784,13 +36038,13 @@ var StatusOutput2 = external_exports.object({
35784
36038
  });
35785
36039
  var StopOutput2 = external_exports.object({ stopped: external_exports.boolean(), pid: external_exports.number().optional() });
35786
36040
  var ResumeOutput = external_exports.object({ resumed: external_exports.boolean() });
35787
- function register31(program3) {
36041
+ function register32(program3) {
35788
36042
  define2(program3, {
35789
36043
  name: "assistantd-start",
35790
36044
  description: "Start the assistant daemon in the foreground. Holds the chat socket on the chosen --os slot. v1 has no launchd integration; rerun under tmux/screen if you want it persistent.",
35791
36045
  output: StartOutput2,
35792
36046
  configure: (cmd) => {
35793
- cmd.option("--os <os>", "chat-channel os slot (default: cfg.defaultChatOs ?? iOS)", parseChatOs).option("--on-kicked <policy>", "when another device takes the slot: pause, immediate, or a delay such as 30s/5m/2h/1d (default: 3m)", parseKickedRecoveryPolicy).option("--debug", "forward wire-level debug events to stderr");
36047
+ cmd.option("--os <os>", "chat-channel slot (default: cfg.defaultChatOs ?? iPad)", parseChatOs).option("--on-kicked <policy>", "when another device takes the slot: pause, immediate, or a delay such as 30s/5m/2h/1d (default: 3m)", parseKickedRecoveryPolicy).option("--debug", "forward wire-level debug events to stderr");
35794
36048
  },
35795
36049
  run: async (cmd) => {
35796
36050
  const opts = cmd.opts();
@@ -36865,7 +37119,7 @@ var StartOutput3 = external_exports.object({
36865
37119
  // honest while leaving the long-running HTTP server in the background.
36866
37120
  closed: external_exports.boolean()
36867
37121
  });
36868
- function register32(program3) {
37122
+ function register33(program3) {
36869
37123
  define2(program3, {
36870
37124
  name: "assistant-ui",
36871
37125
  description: "Start a local HTTP server (default 127.0.0.1:7821) that serves a read-only monitor for the running assistant daemon. Foreground; Ctrl-C to stop.",
@@ -37241,7 +37495,7 @@ var Output28 = external_exports.object({
37241
37495
  output_schema: external_exports.unknown()
37242
37496
  }))
37243
37497
  });
37244
- function register33(program3) {
37498
+ function register34(program3) {
37245
37499
  define2(program3, {
37246
37500
  name: "manifest",
37247
37501
  description: "Emit the full command graph + per-command output JSON Schema. Skill authors should consume this rather than parse --help.",
@@ -37305,6 +37559,7 @@ register30(program2);
37305
37559
  register31(program2);
37306
37560
  register32(program2);
37307
37561
  register33(program2);
37562
+ register34(program2);
37308
37563
  async function main() {
37309
37564
  try {
37310
37565
  await program2.parseAsync(argv);