@simplepush/cli 0.1.1 → 0.2.0

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/main.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.mjs","names":["nodeRandomBytes","formatOption","contentOption","titleOption","memberOption","broadcastOption","orgTopicOption","tagOption","noEncryptOption","sharedOption","formatOption","titleOption","contentOption","textInput","choiceInput","actionInput","sliderInput","photoInput","voiceRecordingInput","fileInput","locationInput","linkOption","fileOption","submitOption","noEncryptOption","markdownOption"],"sources":["../src/errors.ts","../src/services/output.ts","../src/crypto/params.ts","../src/crypto/sodium.ts","../src/services/stores.ts","../src/services/api.ts","../src/services/vault-access.ts","../src/global-options.ts","../src/format.ts","../src/commands/auth.ts","../src/services/sdk.ts","../src/daemon/paths.ts","../src/daemon/server.ts","../src/daemon/transport.ts","../src/since.ts","../src/collect-output.ts","../src/save-files.ts","../src/until.ts","../src/commands/collect.ts","../src/commands/daemon.ts","../src/commands/download.ts","../src/output.ts","../src/commands/events.ts","../src/input-spec.ts","../src/commands/notify.ts","../src/commands/org-encryption.ts","../src/commands/org.ts","../src/files.ts","../src/commands/task.ts","../src/commands/subtask.ts","../src/main.ts"],"sourcesContent":["// The CLI's typed error taxonomy. Every failure a command can produce is a\n// `Data.TaggedError` carried on the Effect error channel; `renderError` at the\n// top of main.ts is the ONLY place errors become stderr text. A handler that\n// has already told the user what went wrong fails with `Aborted` (rendered as\n// nothing) so the process still exits 1 without a duplicate message.\n\nimport { ValidationError } from \"@effect/cli\";\nimport { Terminal } from \"@effect/platform\";\nimport { Data } from \"effect\";\nimport { ParseResult } from \"effect\";\n\n/** No CLI session saved — `sp auth login` hasn't been run (or was logged out). */\nexport class NotLoggedIn extends Data.TaggedError(\"NotLoggedIn\")<{}> {}\n\n/** A personal-credential command was invoked without --api-token / $SP_API_TOKEN. */\nexport class MissingApiToken extends Data.TaggedError(\"MissingApiToken\")<{}> {}\n\n/** The backend answered non-2xx. `action` names the operation for the message. */\nexport class ApiFailure extends Data.TaggedError(\"ApiFailure\")<{\n readonly action: string;\n readonly status: number;\n readonly detail: string;\n}> {}\n\n/** The request never produced an HTTP response (DNS, refused, TLS...). */\nexport class TransportFailure extends Data.TaggedError(\"TransportFailure\")<{\n readonly action: string;\n readonly cause: unknown;\n}> {}\n\n/** A promise-based SDK call or stream failed. */\nexport class SdkFailure extends Data.TaggedError(\"SdkFailure\")<{\n readonly action: string;\n readonly cause: unknown;\n}> {}\n\n/** A libsodium operation failed or was fed mis-sized material. */\nexport class CryptoFailure extends Data.TaggedError(\"CryptoFailure\")<{\n readonly message: string;\n}> {}\n\n/** Vault blob would not decrypt — wrong passphrase or corrupted blob. */\nexport class VaultUnlockFailed extends Data.TaggedError(\"VaultUnlockFailed\")<{}> {}\n\n/** The org has no encryption config enabled. */\nexport class EncryptionDisabled extends Data.TaggedError(\"EncryptionDisabled\")<{}> {}\n\n/** A user-facing validation / usage error with a ready-to-print message. */\nexport class UserError extends Data.TaggedError(\"UserError\")<{\n readonly message: string;\n}> {}\n\n/** The failure has already been reported on stderr — exit 1 silently. */\nexport class Aborted extends Data.TaggedError(\"Aborted\")<{}> {}\n\nconst causeMessage = (cause: unknown): string =>\n cause instanceof Error ? cause.message : String(cause);\n\n/** One error -> one stderr line (sans the `error: ` prefix added by the\n * renderer). `undefined` means \"print nothing\" — the error was already\n * reported (Aborted), or another layer printed it (@effect/cli usage text,\n * an interrupted prompt). */\nexport function renderError(e: unknown): string | undefined {\n if (e instanceof NotLoggedIn) return \"not logged in. Run `sp auth login` first.\";\n if (e instanceof MissingApiToken) return \"an API token is required: pass --api-token or set $SP_API_TOKEN\";\n if (e instanceof ApiFailure) return `${e.action} failed (${e.status}): ${e.detail}`;\n if (e instanceof TransportFailure) return `${e.action} failed: ${causeMessage(e.cause)}`;\n if (e instanceof SdkFailure) return `${e.action} failed: ${causeMessage(e.cause)}`;\n if (e instanceof CryptoFailure) return e.message;\n if (e instanceof VaultUnlockFailed) return \"could not unlock the vault — passphrase is wrong, or the vault is corrupted.\";\n if (e instanceof EncryptionDisabled) return \"encryption is not enabled for this org. Run `org encryption enable` first.\";\n if (e instanceof UserError) return e.message;\n if (e instanceof Aborted) return undefined;\n // @effect/cli already printed the usage text for a bad invocation.\n if (ValidationError.isValidationError(e)) return undefined;\n // Ctrl-C at an interactive prompt — nothing to add.\n if (e instanceof Terminal.QuitException) return undefined;\n if (ParseResult.isParseError(e)) return ParseResult.TreeFormatter.formatErrorSync(e);\n return causeMessage(e);\n}\n","// All terminal output funnels through this service. stderr carries the\n// info/warn/error narration (info gated by --quiet); stdout carries ONLY\n// payload lines (ids, NDJSON envelopes) — that split is the scripting contract.\n//\n// `quiet` lives in a Ref set by each command handler after parsing its flags.\n\nimport { Effect, Ref } from \"effect\";\n\nconst isEpipe = (e: unknown): boolean =>\n e instanceof Error && (e as NodeJS.ErrnoException).code === \"EPIPE\";\n\nexport class CliOutput extends Effect.Service<CliOutput>()(\"cli/CliOutput\", {\n effect: Effect.gen(function* () {\n const quietRef = yield* Ref.make(false);\n\n // Narration is best-effort: a closed stderr must not kill the command.\n const stderr = (line: string) =>\n Effect.sync(() => {\n try {\n process.stderr.write(line + \"\\n\");\n } catch (e) {\n if (!isEpipe(e)) throw e;\n }\n });\n\n return {\n setQuiet: (quiet: boolean) => Ref.set(quietRef, quiet),\n info: (msg: string) =>\n Effect.flatMap(Ref.get(quietRef), (quiet) => (quiet ? Effect.void : stderr(`info: ${msg}`))),\n warn: (msg: string) => stderr(`warn: ${msg}`),\n error: (msg: string) => stderr(`error: ${msg}`),\n /** One payload line on stdout, flushed immediately. EPIPE means the\n * downstream reader is gone (`sp collect | head`, a dead pipeline\n * neighbor): stop writing and exit quietly, like any pipeline citizen. */\n print: (line: string) =>\n Effect.sync(() => {\n try {\n process.stdout.write(line + \"\\n\");\n } catch (e) {\n if (isEpipe(e)) process.exit(0);\n throw e;\n }\n }),\n } as const;\n }),\n}) {}\n","// Pure crypto domain values: KDF parameters, vault content types, and the\n// normalization rules both sides of an invite / passphrase must agree on.\n// Everything touching libsodium lives in the `Sodium` service (./sodium.ts).\n\nimport { createHash } from \"node:crypto\";\nimport { Schema } from \"effect\";\n\n// Argon2id parameters. Stored alongside the vault so we can re-tune them\n// without invalidating existing vaults — every unlock reads the params back\n// out of the server-stored kdf_params blob and feeds them in here.\nexport const KdfParams = Schema.Struct({\n algo: Schema.Literal(\"argon2id\"),\n // Iterations (libsodium opslimit).\n t: Schema.Number,\n // Memory in bytes (libsodium memlimit). 64 MiB by default.\n m: Schema.Number,\n // Parallelism (lane count). libsodium's high-level wrapper fixes this at 1\n // and ignores any other value; kept for forward compat and for the\n // server-side params record.\n p: Schema.Number,\n});\nexport type KdfParams = typeof KdfParams.Type;\n\nexport const DEFAULT_KDF_PARAMS: KdfParams = {\n algo: \"argon2id\",\n t: 3,\n m: 64 * 1024 * 1024,\n p: 1,\n};\n\n// 32 bytes — long enough to key the XChaCha20-Poly1305 AEAD used for the\n// vault blob.\nexport const VAULT_KEY_BYTES = 32;\n\n// The BIP39 English wordlist has exactly 2048 entries → 11 bits per word.\n// 8 words ≈ 88 bits, the design target.\nexport const DEFAULT_WORD_COUNT = 8;\n\nconst MasterKeySchema = Schema.Struct({\n version: Schema.Number,\n key: Schema.Uint8ArrayFromSelf,\n});\nexport type MasterKey = typeof MasterKeySchema.Type;\n\n// Plaintext contents of the org encryption vault. Encrypted under `vault_key`\n// (derived from the org passphrase) and stored server-side as a single blob.\n//\n// `adminPrivateKey` plus `masterKeyCurrent` are what any admin's CLI needs\n// to operate; `masterKeyHistory` lets admins decrypt notifications encrypted\n// under prior versions and re-wrap them to new devices on demand.\nexport const VaultContents = Schema.Struct({\n adminPublicKey: Schema.Uint8ArrayFromSelf,\n adminPrivateKey: Schema.Uint8ArrayFromSelf,\n masterKeyCurrent: MasterKeySchema,\n masterKeyHistory: Schema.Array(MasterKeySchema),\n});\nexport type VaultContents = typeof VaultContents.Type;\n\n// Serialized vault shape (inside the encrypted blob): bytes base64-encoded\n// plus a format version so the schema can evolve without ambiguity.\nconst MasterKeyJson = Schema.Struct({\n version: Schema.Number,\n key: Schema.Uint8ArrayFromBase64,\n});\n\nexport const VaultJson = Schema.Struct({\n formatVersion: Schema.Literal(1),\n adminPublicKey: Schema.Uint8ArrayFromBase64,\n adminPrivateKey: Schema.Uint8ArrayFromBase64,\n masterKeyCurrent: MasterKeyJson,\n masterKeyHistory: Schema.Array(MasterKeyJson),\n});\n\n// Light normalization for human-typed input: trim, collapse whitespace,\n// lowercase. Lets the user paste \" Foo bar\\nbaz \" and have it match\n// \"foo bar baz\". Does not change semantics if input is already canonical.\nexport function normalizePassphrase(input: string): string {\n return input.trim().toLowerCase().split(/\\s+/).join(\" \");\n}\n\n// Normalization mirrors backend/util/InviteCode.normalize:\n// trim → strip dashes and whitespace → uppercase.\n// So \"abcd-efgh\", \"ABCDEFGH\", and \"abcd efgh\" all hash identically. Both\n// sides MUST agree on this rule or HMAC verification silently fails.\nexport function normalizeInviteCode(input: string): string {\n return input.trim().toUpperCase().replace(/[-\\s]/g, \"\");\n}\n\n// Hash must match the backend's `InviteCode.hash`:\n// sha256(normalize(code)).hex()\n// The backend only ever sees this hash — the admin's CLI is the sole holder\n// of the cleartext, so a compromised backend can't later forge an HMAC for a\n// substituted device pubkey at sync time.\nexport function hashInviteCode(plain: string): string {\n return createHash(\"sha256\").update(normalizeInviteCode(plain), \"utf8\").digest(\"hex\");\n}\n","// The `Sodium` service owns libsodium-wrappers-sumo (async wasm init happens\n// once, in the layer, instead of a top-level await every importer pays for)\n// and exposes the CLI's crypto operations as Effects failing with a typed\n// `CryptoFailure`. The sumo variant is required for crypto_pwhash (Argon2id),\n// which the standard build omits.\n\nimport { randomBytes as nodeRandomBytes } from \"node:crypto\";\nimport _sodium from \"libsodium-wrappers-sumo\";\nimport { wordlist } from \"@scure/bip39/wordlists/english.js\";\nimport { Effect, Schema } from \"effect\";\n\nimport { CryptoFailure } from \"../errors.js\";\nimport {\n DEFAULT_KDF_PARAMS,\n DEFAULT_WORD_COUNT,\n VAULT_KEY_BYTES,\n VaultJson,\n normalizeInviteCode,\n type KdfParams,\n type VaultContents,\n} from \"./params.js\";\n\nexport interface AdminKeyPair {\n readonly publicKey: Uint8Array;\n readonly privateKey: Uint8Array;\n}\n\nconst WRAP_NONCE_BYTES = 24;\nconst VAULT_NONCE_BYTES = 24;\n\n// Crockford-base32-ish invite alphabet: 32 chars, no 0/1/I/L/O.\n// 256 % 32 = 0 → unbiased modulo.\nconst InviteAlphabet = \"ABCDEFGHJKMNPQRSTUVWXYZ23456789\";\nconst InviteGroupSize = 4;\nconst InviteGroups = 2;\n\nconst encodeVaultJson = Schema.encodeSync(VaultJson);\nconst decodeVaultJson = Schema.decodeUnknownSync(Schema.parseJson(VaultJson));\n\nexport class Sodium extends Effect.Service<Sodium>()(\"cli/Sodium\", {\n effect: Effect.gen(function* () {\n const sodium = yield* Effect.promise(() => _sodium.ready.then(() => _sodium));\n\n const fail = (message: string) => new CryptoFailure({ message });\n const attempt = <A>(message: string, f: () => A): Effect.Effect<A, CryptoFailure> =>\n Effect.try({ try: f, catch: (e) => fail(e instanceof CryptoFailure ? e.message : message) });\n\n // Plain (non-Effect) codecs — deterministic, infallible on our inputs.\n const toB64 = (bytes: Uint8Array): string => sodium.to_base64(bytes, sodium.base64_variants.ORIGINAL);\n const fromB64 = (s: string): Uint8Array => sodium.from_base64(s, sodium.base64_variants.ORIGINAL);\n\n return {\n toB64,\n fromB64,\n\n randomBytes: (length: number) => Effect.sync(() => sodium.randombytes_buf(length)),\n\n // 32-byte salt for vault-key derivation. The design stores it server-side\n // so it can be rotated independently of the passphrase.\n generateVaultSalt: Effect.sync(() => sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES)),\n\n // Symmetric key for AEAD: 32 bytes, used as `master_key_vN`.\n generateMasterKey: Effect.sync(() =>\n sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES),\n ),\n\n // X25519 keypair for `crypto_box` wraps of master_key to device pubkeys.\n generateAdminKeyPair: Effect.sync((): AdminKeyPair => {\n const kp = sodium.crypto_box_keypair();\n return { publicKey: kp.publicKey, privateKey: kp.privateKey };\n }),\n\n // We deliberately do NOT use BIP39 mnemonic encoding (which folds in a\n // checksum and only allows specific word counts) — this is a passphrase,\n // not a wallet seed.\n generatePassphrase: (wordCount: number = DEFAULT_WORD_COUNT) =>\n Effect.gen(function* () {\n if (!Number.isInteger(wordCount) || wordCount < 1) {\n return yield* fail(`wordCount must be a positive integer (got ${wordCount})`);\n }\n if (wordlist.length !== 2048) {\n return yield* fail(`unexpected wordlist length: ${wordlist.length}`);\n }\n const words: string[] = [];\n for (let i = 0; i < wordCount; i++) {\n // randombytes_uniform does unbiased rejection sampling under the hood.\n words.push(wordlist[sodium.randombytes_uniform(wordlist.length)]!);\n }\n return words.join(\" \");\n }),\n\n // Cleartext invite code (uses node:crypto randomness, grouped for humans).\n generateInviteCode: Effect.sync(() => {\n const bytes = nodeRandomBytes(InviteGroupSize * InviteGroups);\n const groups: string[] = [];\n for (let g = 0; g < InviteGroups; g++) {\n let group = \"\";\n for (let i = 0; i < InviteGroupSize; i++) {\n group += InviteAlphabet.charAt(bytes[g * InviteGroupSize + i]! % InviteAlphabet.length);\n }\n groups.push(group);\n }\n return groups.join(\"-\");\n }),\n\n deriveVaultKey: (passphrase: string, salt: Uint8Array, params: KdfParams = DEFAULT_KDF_PARAMS) =>\n Effect.gen(function* () {\n if ((params.algo as string) !== \"argon2id\") {\n return yield* fail(`unsupported KDF algorithm: ${params.algo}`);\n }\n if (salt.length !== sodium.crypto_pwhash_SALTBYTES) {\n return yield* fail(`salt must be ${sodium.crypto_pwhash_SALTBYTES} bytes (got ${salt.length})`);\n }\n return yield* attempt(\"key derivation failed\", () =>\n sodium.crypto_pwhash(VAULT_KEY_BYTES, passphrase, salt, params.t, params.m, sodium.crypto_pwhash_ALG_ARGON2ID13),\n );\n }),\n\n encryptVault: (contents: VaultContents, vaultKey: Uint8Array) =>\n Effect.gen(function* () {\n if (vaultKey.length !== sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) {\n return yield* fail(`vaultKey must be ${sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES} bytes`);\n }\n return yield* attempt(\"vault encryption failed\", () => {\n const json = encodeVaultJson({ formatVersion: 1, ...contents });\n const plaintext = new TextEncoder().encode(JSON.stringify(json));\n const nonce = sodium.randombytes_buf(VAULT_NONCE_BYTES);\n const ct = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, null, null, nonce, vaultKey);\n // Concatenate nonce || ciphertext so callers only persist one blob.\n const out = new Uint8Array(nonce.length + ct.length);\n out.set(nonce, 0);\n out.set(ct, nonce.length);\n return out;\n });\n }),\n\n decryptVault: (blob: Uint8Array, vaultKey: Uint8Array) =>\n Effect.gen(function* () {\n if (vaultKey.length !== sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) {\n return yield* fail(`vaultKey must be ${sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES} bytes`);\n }\n if (blob.length < VAULT_NONCE_BYTES + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) {\n return yield* fail(\"vault blob is truncated\");\n }\n return yield* attempt(\"vault decryption failed\", (): VaultContents => {\n const nonce = blob.slice(0, VAULT_NONCE_BYTES);\n const ct = blob.slice(VAULT_NONCE_BYTES);\n // crypto_aead_*_decrypt throws on auth-tag mismatch (wrong key, tampered blob).\n const plaintext = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, ct, null, nonce, vaultKey);\n return decodeVaultJson(new TextDecoder().decode(plaintext));\n });\n }),\n\n // Wraps a master key from the admin to a single device.\n //\n // `crypto_box_easy` (X25519 + XSalsa20 + Poly1305) authenticates the\n // sender, so the device knows the wrap came from someone holding\n // `adminPrivateKey` — the malicious-backend \"swap a wrap\" attack is\n // rejected at the unwrap step because the auth tag won't verify against\n // the pinned admin pubkey. We deliberately do NOT use `crypto_box_seal`\n // (anonymous), which would let the backend forge wraps to keys it controls.\n wrapMasterKey: (masterKey: Uint8Array, adminPrivateKey: Uint8Array, devicePublicKey: Uint8Array) =>\n Effect.gen(function* () {\n if (adminPrivateKey.length !== sodium.crypto_box_SECRETKEYBYTES) {\n return yield* fail(`adminPrivateKey must be ${sodium.crypto_box_SECRETKEYBYTES} bytes`);\n }\n if (devicePublicKey.length !== sodium.crypto_box_PUBLICKEYBYTES) {\n return yield* fail(`devicePublicKey must be ${sodium.crypto_box_PUBLICKEYBYTES} bytes`);\n }\n return yield* attempt(\"master-key wrap failed\", () => {\n const nonce = sodium.randombytes_buf(WRAP_NONCE_BYTES);\n const ct = sodium.crypto_box_easy(masterKey, nonce, devicePublicKey, adminPrivateKey);\n const out = new Uint8Array(nonce.length + ct.length);\n out.set(nonce, 0);\n out.set(ct, nonce.length);\n return out;\n });\n }),\n\n unwrapMasterKey: (blob: Uint8Array, devicePrivateKey: Uint8Array, adminPublicKey: Uint8Array) =>\n Effect.gen(function* () {\n if (devicePrivateKey.length !== sodium.crypto_box_SECRETKEYBYTES) {\n return yield* fail(`devicePrivateKey must be ${sodium.crypto_box_SECRETKEYBYTES} bytes`);\n }\n if (adminPublicKey.length !== sodium.crypto_box_PUBLICKEYBYTES) {\n return yield* fail(`adminPublicKey must be ${sodium.crypto_box_PUBLICKEYBYTES} bytes`);\n }\n if (blob.length < WRAP_NONCE_BYTES + sodium.crypto_box_MACBYTES) {\n return yield* fail(\"wrapped blob is truncated\");\n }\n return yield* attempt(\"master-key unwrap failed\", () =>\n sodium.crypto_box_open_easy(blob.slice(WRAP_NONCE_BYTES), blob.slice(0, WRAP_NONCE_BYTES), adminPublicKey, devicePrivateKey),\n );\n }),\n\n // HMAC binding between an invite code and a device pubkey, used to confirm\n // that the pubkey was submitted by whoever actually held the invite.\n // Normalizes the code first (both sides MUST agree on that rule).\n // Single-call `crypto_auth_hmacsha256(message, key)` requires a fixed-length\n // (32-byte) key; the streaming API accepts variable-length keys (proper\n // RFC 2104 HMAC behavior), which is what an 8-char invite code needs.\n hmacInviteBinding: (inviteCode: string, devicePublicKey: Uint8Array): Uint8Array => {\n const state = sodium.crypto_auth_hmacsha256_init(normalizeInviteCode(inviteCode));\n sodium.crypto_auth_hmacsha256_update(state, devicePublicKey);\n return sodium.crypto_auth_hmacsha256_final(state);\n },\n\n // Constant-time equality for HMAC verification. Critical for not leaking\n // timing information when the CLI brute-forces its invite-code list\n // against each device row.\n constantTimeEqual: (a: Uint8Array, b: Uint8Array): boolean =>\n a.length === b.length && sodium.memcmp(a, b),\n } as const;\n }),\n}) {}\n","// Local persistence under ~/.config/simplepush (or %APPDATA%\\simplepush on\n// Windows): auth.json (CLI session), vault.json (unlocked org-vault cache),\n// invites.json (issued invite cleartexts). All three are Schema-validated\n// JSON files with mode 0600 in a 0700 dir — same trust class.\n\nimport { homedir } from \"node:os\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport type { PlatformError } from \"@effect/platform/Error\";\nimport { Effect, Option, ParseResult, Redacted, Schema } from \"effect\";\n\nimport { VaultContents } from \"../crypto/index.js\";\n\nexport function configDir(): string {\n if (process.platform === \"win32\") {\n const appData = process.env.APPDATA ?? `${homedir()}/AppData/Roaming`;\n return `${appData}/simplepush`;\n }\n const xdg = process.env.XDG_CONFIG_HOME;\n return xdg ? `${xdg}/simplepush` : `${homedir()}/.config/simplepush`;\n}\n\nconst isNotFound = (e: PlatformError | ParseResult.ParseError): boolean =>\n e._tag === \"SystemError\" && e.reason === \"NotFound\";\n\n/** One Schema-validated JSON file with 0600 perms. `load` distinguishes\n * \"absent\" (Option.none) from real IO/decode failures. */\nconst jsonFile = <A, I>(fileName: string, schema: Schema.Schema<A, I>) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n const decode = Schema.decodeUnknown(Schema.parseJson(schema));\n const encode = Schema.encode(schema);\n const filePath = path.join(configDir(), fileName);\n\n const load: Effect.Effect<Option.Option<A>, PlatformError | ParseResult.ParseError> = fs\n .readFileString(filePath)\n .pipe(\n Effect.flatMap(decode),\n Effect.map(Option.some),\n Effect.catchIf(isNotFound, () => Effect.succeed(Option.none<A>())),\n );\n\n const save = (value: A): Effect.Effect<string, PlatformError | ParseResult.ParseError> =>\n Effect.gen(function* () {\n yield* fs.makeDirectory(configDir(), { recursive: true }).pipe(Effect.ignore);\n yield* fs.chmod(configDir(), 0o700).pipe(Effect.ignore);\n const encoded = yield* encode(value);\n yield* fs.writeFileString(filePath, JSON.stringify(encoded, null, 2));\n // writeFile only enforces mode on creation; chmod again so re-saving an\n // existing (looser-permissioned) file tightens it.\n if (process.platform !== \"win32\") yield* fs.chmod(filePath, 0o600);\n return filePath;\n });\n\n /** Removes the file; false when there was nothing to remove. */\n const clear: Effect.Effect<boolean, PlatformError> = fs.remove(filePath).pipe(\n Effect.as(true),\n Effect.catchIf(isNotFound, () => Effect.succeed(false)),\n );\n\n return { filePath, load, save, clear } as const;\n });\n\n// ---------- auth ----------\n\nexport const StoredAuth = Schema.Struct({\n baseUrl: Schema.String,\n // Redacted so the session token can't leak through logs / error output.\n token: Schema.Redacted(Schema.String),\n loggedInAt: Schema.String,\n});\nexport type StoredAuth = typeof StoredAuth.Type;\n\nexport const bearerToken = (auth: StoredAuth): string => Redacted.value(auth.token);\n\nexport class AuthStore extends Effect.Service<AuthStore>()(\"cli/AuthStore\", {\n effect: jsonFile(\"auth.json\", StoredAuth),\n}) {}\n\n// ---------- org vault cache ----------\n\n// On-disk vault.json shape: base64-encoded key material plus a format version.\n// Stable — existing files must keep decoding.\nconst StoredVault = Schema.Struct({\n formatVersion: Schema.Literal(1),\n adminPublicKeyB64: Schema.Uint8ArrayFromBase64,\n adminPrivateKeyB64: Schema.Uint8ArrayFromBase64,\n masterKeyCurrent: Schema.Struct({ version: Schema.Number, keyB64: Schema.Uint8ArrayFromBase64 }),\n masterKeyHistory: Schema.Array(Schema.Struct({ version: Schema.Number, keyB64: Schema.Uint8ArrayFromBase64 })),\n});\n\nconst VaultFromStored = Schema.transform(StoredVault, VaultContents, {\n strict: true,\n decode: (s): VaultContents => ({\n adminPublicKey: s.adminPublicKeyB64,\n adminPrivateKey: s.adminPrivateKeyB64,\n masterKeyCurrent: { version: s.masterKeyCurrent.version, key: s.masterKeyCurrent.keyB64 },\n masterKeyHistory: s.masterKeyHistory.map((m) => ({ version: m.version, key: m.keyB64 })),\n }),\n encode: (v: VaultContents) => ({\n formatVersion: 1 as const,\n adminPublicKeyB64: v.adminPublicKey,\n adminPrivateKeyB64: v.adminPrivateKey,\n masterKeyCurrent: { version: v.masterKeyCurrent.version, keyB64: v.masterKeyCurrent.key },\n masterKeyHistory: v.masterKeyHistory.map((m) => ({ version: m.version, keyB64: m.key })),\n }),\n});\n\nexport class VaultStore extends Effect.Service<VaultStore>()(\"cli/VaultStore\", {\n effect: jsonFile(\"vault.json\", VaultFromStored),\n}) {}\n\n// ---------- issued invites ----------\n\n// The CLI keeps issued invite cleartexts so a later `org encryption sync` can\n// verify `HMAC(invite_code, device_pubkey)` against each device row — the\n// backend only ever holds hash(code), deliberately.\nexport const IssuedInvite = Schema.Struct({\n code: Schema.String, // plaintext, exactly as shown to the admin\n name: Schema.String,\n role: Schema.Literal(\"member\", \"admin\"),\n issuedAt: Schema.String, // ISO\n expiresAt: Schema.String, // ISO\n});\nexport type IssuedInvite = typeof IssuedInvite.Type;\n\nconst InvitesFile = Schema.Struct({ invites: Schema.Array(IssuedInvite) });\n\nexport class InviteStore extends Effect.Service<InviteStore>()(\"cli/InviteStore\", {\n effect: Effect.gen(function* () {\n const file = yield* jsonFile(\"invites.json\", InvitesFile);\n\n const loadAll = file.load.pipe(\n Effect.map(Option.match({ onNone: () => [] as ReadonlyArray<IssuedInvite>, onSome: (f) => f.invites })),\n // A corrupt file behaves like an empty one.\n Effect.catchTag(\"ParseError\", () => Effect.succeed([] as ReadonlyArray<IssuedInvite>)),\n );\n\n return {\n filePath: file.filePath,\n clear: file.clear,\n\n /** Drops any prior entry for the same plaintext code so re-issuance under\n * the same code (shouldn't happen, but defensively) can't duplicate. */\n append: (invite: IssuedInvite) =>\n Effect.gen(function* () {\n const existing = yield* loadAll;\n yield* file.save({ invites: [...existing.filter((i) => i.code !== invite.code), invite] });\n }),\n\n /** Currently-valid invites only — past-expiration entries are pruned from\n * disk as a side effect, shrinking the HMAC candidate set per device. */\n listValid: Effect.gen(function* () {\n const now = new Date();\n const all = yield* loadAll;\n const fresh = all.filter((i) => new Date(i.expiresAt) > now);\n if (fresh.length !== all.length) yield* file.save({ invites: fresh });\n return fresh;\n }),\n\n /** Removes the given code (post-sync consumption). Idempotent. */\n consume: (code: string) =>\n Effect.gen(function* () {\n const all = yield* loadAll;\n const next = all.filter((i) => i.code !== code);\n if (next.length === all.length) return false;\n yield* file.save({ invites: next });\n return true;\n }),\n } as const;\n }),\n}) {}\n","// Backend HTTP for bearer-session commands (`sp org …`, org sends). Wraps the\n// platform HttpClient with: base URL + Authorization from the saved CLI\n// session (AuthStore), the standard `{error, msg}` failure-body parse into a\n// typed ApiFailure, and Schema-decoded success bodies.\n\nimport { HttpBody, HttpClient, HttpClientRequest, HttpClientResponse } from \"@effect/platform\";\nimport { Effect, Option, Schema } from \"effect\";\n\nimport { ApiFailure, NotLoggedIn, TransportFailure } from \"../errors.js\";\nimport { AuthStore, bearerToken, type StoredAuth } from \"./stores.js\";\n\nconst ErrorBody = Schema.Struct({ error: Schema.String, msg: Schema.String });\nconst decodeErrorBody = Schema.decodeUnknownOption(Schema.parseJson(ErrorBody));\n\nconst trimSlash = (url: string): string => url.replace(/\\/+$/, \"\");\n\nexport class Api extends Effect.Service<Api>()(\"cli/Api\", {\n dependencies: [AuthStore.Default],\n effect: Effect.gen(function* () {\n const http = yield* HttpClient.HttpClient;\n const store = yield* AuthStore;\n\n /** The saved CLI session, or NotLoggedIn. */\n const session: Effect.Effect<StoredAuth, NotLoggedIn> = store.load.pipe(\n Effect.orElseSucceed(() => Option.none<StoredAuth>()),\n Effect.flatMap(\n Option.match({\n onNone: () => Effect.fail(new NotLoggedIn()),\n onSome: Effect.succeed,\n }),\n ),\n );\n\n /** Extract the human-readable message from a failed response: the standard\n * `{error, msg}` body when present, the raw body text otherwise. */\n const failWith = (action: string, res: HttpClientResponse.HttpClientResponse) =>\n res.text.pipe(\n Effect.orElseSucceed(() => \"\"),\n Effect.flatMap((body) => {\n const parsed = decodeErrorBody(body);\n const detail = Option.isSome(parsed) && parsed.value.msg ? parsed.value.msg : body || `HTTP ${res.status}`;\n return Effect.fail(new ApiFailure({ action, status: res.status, detail }));\n }),\n );\n\n /** Authenticated request; resolves with the (scoped) response once the\n * status is 2xx, fails with ApiFailure/TransportFailure otherwise. */\n const request = (action: string, method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\", pathname: string, body?: unknown) =>\n Effect.gen(function* () {\n const auth = yield* session;\n const base = HttpClientRequest.make(method)(`${trimSlash(auth.baseUrl)}${pathname}`).pipe(\n HttpClientRequest.bearerToken(bearerToken(auth)),\n );\n const req =\n body === undefined\n ? base\n : HttpClientRequest.setBody(base, HttpBody.unsafeJson(body));\n const res = yield* http.execute(req).pipe(\n Effect.mapError((cause) => new TransportFailure({ action, cause })),\n );\n if (res.status >= 400) return yield* failWith(action, res);\n return res;\n });\n\n /** Request + Schema-decode the JSON success body. */\n const requestJson = <A, I>(\n action: string,\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\",\n pathname: string,\n schema: Schema.Schema<A, I>,\n body?: unknown,\n ) =>\n Effect.scoped(\n request(action, method, pathname, body).pipe(\n Effect.flatMap(HttpClientResponse.schemaBodyJson(schema)),\n ),\n );\n\n return {\n session,\n getJson: <A, I>(action: string, pathname: string, schema: Schema.Schema<A, I>) =>\n requestJson(action, \"GET\", pathname, schema),\n postJson: <A, I>(action: string, pathname: string, schema: Schema.Schema<A, I>, body?: unknown) =>\n requestJson(action, \"POST\", pathname, schema, body),\n putJson: <A, I>(action: string, pathname: string, schema: Schema.Schema<A, I>, body?: unknown) =>\n requestJson(action, \"PUT\", pathname, schema, body),\n /** Fire-and-forget variants for endpoints whose response body we ignore. */\n post: (action: string, pathname: string, body?: unknown) =>\n Effect.scoped(Effect.asVoid(request(action, \"POST\", pathname, body))),\n put: (action: string, pathname: string, body?: unknown) =>\n Effect.scoped(Effect.asVoid(request(action, \"PUT\", pathname, body))),\n delete: (action: string, pathname: string) =>\n Effect.scoped(Effect.asVoid(request(action, \"DELETE\", pathname))),\n\n /** Unauthenticated POST against an explicit base URL (the `sp auth login`\n * flows run before any session exists). Returns status + body text so\n * callers can branch on OAuth-style error payloads. */\n unauthedPost: (action: string, url: string, body?: unknown) =>\n Effect.scoped(\n Effect.gen(function* () {\n const base = HttpClientRequest.post(url);\n const req = body === undefined ? base : HttpClientRequest.setBody(base, HttpBody.unsafeJson(body));\n const res = yield* http.execute(req).pipe(\n Effect.mapError((cause) => new TransportFailure({ action, cause })),\n );\n const text = yield* res.text.pipe(Effect.orElseSucceed(() => \"\"));\n return { status: res.status, body: text };\n }),\n ),\n } as const;\n }),\n}) {}\n","// Single entry point for reading the decrypted org vault. Prompts for the\n// passphrase on demand when the local plaintext cache is missing, so callers\n// (`sync`, `notify`, rotations) don't have to coordinate an explicit `unlock`\n// step: ask once when needed, keep around until explicitly cleared.\n\nimport { Prompt } from \"@effect/cli\";\nimport { Effect, Redacted, Schema } from \"effect\";\n\nimport { EncryptionDisabled, VaultUnlockFailed } from \"../errors.js\";\nimport { KdfParams, normalizePassphrase, type VaultContents } from \"../crypto/index.js\";\nimport { Sodium } from \"../crypto/sodium.js\";\nimport { Api } from \"./api.js\";\nimport { CliOutput } from \"./output.js\";\nimport { VaultStore } from \"./stores.js\";\n\n// The four config fields are ABSENT (not null) whenever enabled=false —\n// zio-json omits None on encode. Gate on `enabled`, not on key presence.\nexport const OrgEncryptionConfig = Schema.Struct({\n enabled: Schema.Boolean,\n adminPubkeyB64: Schema.optional(Schema.NullOr(Schema.String)),\n vaultBlobB64: Schema.optional(Schema.NullOr(Schema.String)),\n vaultSaltB64: Schema.optional(Schema.NullOr(Schema.String)),\n kdfParams: Schema.optional(Schema.NullOr(KdfParams)),\n});\nexport type OrgEncryptionConfig = typeof OrgEncryptionConfig.Type;\n\n/** An enabled config, with the unlock material guaranteed present. */\nexport interface EnabledEncryptionConfig {\n readonly vaultBlobB64: string;\n readonly vaultSaltB64: string;\n readonly kdfParams: KdfParams;\n}\n\nasync function readWholeStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks).toString(\"utf8\").replace(/\\r?\\n$/, \"\");\n}\n\nexport class VaultAccess extends Effect.Service<VaultAccess>()(\"cli/VaultAccess\", {\n dependencies: [Api.Default, VaultStore.Default, Sodium.Default, CliOutput.Default],\n effect: Effect.gen(function* () {\n const api = yield* Api;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n const out = yield* CliOutput;\n\n /** Hidden-input passphrase prompt on a TTY; piped stdin passes through\n * unchanged (trailing newline trimmed) for scripted flows. */\n const readPassphrase = (message: string) =>\n process.stdin.isTTY\n ? Prompt.run(Prompt.password({ message })).pipe(Effect.map(Redacted.value))\n : Effect.promise(readWholeStdin);\n\n const fetchConfig = api.getJson(\"fetch encryption config\", \"/v1/org/encryption\", OrgEncryptionConfig);\n\n /** Narrows a config to its enabled shape or fails EncryptionDisabled. */\n const requireEnabled = (cfg: OrgEncryptionConfig) =>\n !cfg.enabled || !cfg.vaultBlobB64 || !cfg.vaultSaltB64 || !cfg.kdfParams\n ? Effect.fail(new EncryptionDisabled())\n : Effect.succeed<EnabledEncryptionConfig>({\n vaultBlobB64: cfg.vaultBlobB64,\n vaultSaltB64: cfg.vaultSaltB64,\n kdfParams: cfg.kdfParams,\n });\n\n /** Prompt → Argon2id → AEAD-open. Any decrypt failure collapses to\n * VaultUnlockFailed (wrong passphrase and corrupt blob are\n * indistinguishable by design of the AEAD). */\n const unlock = (cfg: EnabledEncryptionConfig, promptText: string) =>\n Effect.gen(function* () {\n const passphrase = yield* readPassphrase(promptText);\n const derived = yield* sodium\n .deriveVaultKey(normalizePassphrase(passphrase), sodium.fromB64(cfg.vaultSaltB64), cfg.kdfParams)\n .pipe(Effect.mapError(() => new VaultUnlockFailed()));\n const vault = yield* sodium\n .decryptVault(sodium.fromB64(cfg.vaultBlobB64), derived)\n .pipe(Effect.mapError(() => new VaultUnlockFailed()));\n return { vault, vaultKey: derived };\n });\n\n /** True when the cached vault belongs to THIS org's current encryption\n * config: the config is enabled and its admin pubkey matches the cache's.\n * A cache left behind by a different login (or a re-enabled config) fails\n * this and must not be used — sends encrypted under it would be\n * undecryptable by every recipient device. */\n const cacheMatches = (vault: VaultContents, cfg: OrgEncryptionConfig): boolean =>\n cfg.enabled &&\n typeof cfg.adminPubkeyB64 === \"string\" &&\n sodium.constantTimeEqual(vault.adminPublicKey, sodium.fromB64(cfg.adminPubkeyB64));\n\n /** The decrypted vault: the local cache when it matches the org's current\n * config, otherwise prompt, unlock, and cache for next time. The config is\n * fetched even on the cache path — one GET per encrypted send buys the\n * staleness check above (without it, a `vault.json` surviving a re-login\n * would silently encrypt under the previous org's key). */\n const getOrPrompt = Effect.gen(function* () {\n const cached = yield* vaultStore.load;\n const cfg = yield* fetchConfig;\n if (cached._tag === \"Some\") {\n if (cacheMatches(cached.value, cfg)) return cached.value;\n yield* vaultStore.clear;\n yield* out.warn(\"cached vault doesn't match this org's encryption config (stale login?) — cleared it\");\n }\n const enabled = yield* requireEnabled(cfg);\n const { vault } = yield* unlock(enabled, \"Org encryption passphrase: \");\n yield* vaultStore.save(vault);\n return vault;\n });\n\n return {\n readPassphrase,\n fetchConfig,\n requireEnabled,\n getOrPrompt,\n\n /** The auto-encrypt decision for org sends: the unlocked vault when the\n * org has encryption (prompting inline on a fresh machine rather than\n * silently going plaintext), `undefined` when encryption is disabled or\n * the caller opted out with --no-encrypt. */\n forSendOrPlaintext: (noEncrypt: boolean) =>\n noEncrypt\n ? Effect.succeed<VaultContents | undefined>(undefined)\n : getOrPrompt.pipe(\n Effect.map((vault): VaultContents | undefined => vault),\n Effect.catchTag(\"EncryptionDisabled\", () => Effect.succeed<VaultContents | undefined>(undefined)),\n ),\n\n /** Rotation needs `vault_key` itself (not just the decrypted contents) to\n * re-encrypt the updated vault, and the cache deliberately doesn't store\n * it — so this ALWAYS re-prompts, which is also defensible on its own\n * merits for a privileged op. */\n unlockForRotation: Effect.gen(function* () {\n const cfg = yield* fetchConfig.pipe(Effect.flatMap(requireEnabled));\n return yield* unlock(cfg, \"Org encryption passphrase (required for rotation): \");\n }),\n } as const;\n }),\n}) {}\n\nexport type { VaultContents };\n","// Shared `--topic`, `--api-token`, etc. that every subcommand can take.\n// Env fallbacks ($SP_API_TOKEN, $SP_BASE_URL, $SP_TAG) are\n// wired declaratively via `Options.withFallbackConfig`, and value validation\n// happens at parse time via `Options.mapTryCatch` — a bad `--password` fails\n// as a usage error before any handler runs.\n\nimport { HelpDoc, Options } from \"@effect/cli\";\nimport { Config, Effect, Option } from \"effect\";\n\nimport { MissingApiToken } from \"./errors.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.simplepu.sh\";\n\nconst toHelp = (e: unknown): HelpDoc.HelpDoc => HelpDoc.p(e instanceof Error ? e.message : String(e));\n\nexport const topicOption = Options.text(\"topic\").pipe(\n Options.withAlias(\"t\"),\n Options.withDescription(\"Topic to send to (`task`) or filter on (`events`, repeatable). Omit on `task` for a self-send to your own devices.\"),\n Options.repeated,\n);\n\nexport const apiTokenOption = Options.text(\"api-token\").pipe(\n Options.withDescription(\n \"API token. Required for `events` and `get`; `collect` falls back to the logged-in org session when omitted. Defaults to $SP_API_TOKEN.\",\n ),\n Options.withFallbackConfig(Config.string(\"SP_API_TOKEN\")),\n Options.optional,\n);\n\n/** Personal-credential commands need the token; fail typed when absent. */\nexport const requireApiToken = (token: Option.Option<string>): Effect.Effect<string, MissingApiToken> =>\n Option.match(token, {\n onNone: () => Effect.fail(new MissingApiToken()),\n onSome: Effect.succeed,\n });\n\n/** A `--password` value: `password@topic` is a topic password (split on the\n * LAST `@`, so the password itself may contain `@`); a bare value is the\n * account default password. The SDK rejects more than one default. */\nexport type PasswordFlag = [password: string, topic: string] | string;\n\nexport function parsePasswordFlag(value: string): PasswordFlag {\n const at = value.lastIndexOf(\"@\");\n if (at === -1) return value; // bare → account default password\n const password = value.slice(0, at);\n const topic = value.slice(at + 1);\n if (!password || !topic) {\n throw new Error(\n `invalid --password \\`${value}\\`: use \\`password@topic\\` for a topic password, or a bare password for the account default`,\n );\n }\n return [password, topic];\n}\n\nexport const passwordOption = Options.text(\"password\").pipe(\n Options.withAlias(\"p\"),\n Options.withDescription(\n \"End-to-end encryption password. `password@topic` sets a topic's password \" +\n \"(encrypts sends to it and decrypts its content); a bare `password` is your \" +\n \"account default (decrypts your submissions). Repeatable.\",\n ),\n Options.repeated,\n // Per-value validation happens on the collected array — `repeated` only\n // composes on a bare option, so the map goes after it.\n Options.mapTryCatch((values) => values.map(parsePasswordFlag), toHelp),\n);\n\n/** True when the SDK will E2E-encrypt a send: a `password@topic` pair matching\n * the topic, or (for a note-to-self) a bare account-default password. */\nexport const willEncrypt = (passwords: ReadonlyArray<PasswordFlag>, topic: string | undefined): boolean =>\n topic !== undefined\n ? passwords.some((p) => Array.isArray(p) && p[1] === topic)\n : passwords.some((p) => typeof p === \"string\");\n\nexport const baseUrlOption = Options.text(\"base-url\").pipe(\n Options.withDescription(`API base URL. Defaults to $SP_BASE_URL or ${DEFAULT_BASE_URL}.`),\n Options.withFallbackConfig(Config.string(\"SP_BASE_URL\")),\n Options.withDefault(DEFAULT_BASE_URL),\n);\n\nexport const quietOption = Options.boolean(\"quiet\").pipe(\n Options.withAlias(\"q\"),\n Options.withDescription(\"Suppress informational output, only print payloads.\"),\n);\n\n/** Shared helper for repeatable validated text options. */\nexport const mappedText = <B>(name: string, parse: (raw: string) => B) =>\n Options.text(name).pipe(Options.mapTryCatch(parse, toHelp));\n","// Tiny presentation helpers shared across commands.\n\nexport function formatInstant(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return iso;\n return d.toISOString().replace(\"T\", \" \").replace(/\\..+/, \" UTC\");\n}\n\nexport function maskToken(t: string): string {\n if (t.length <= 8) return \"*\".repeat(t.length);\n return `${t.slice(0, 4)}…${t.slice(-4)}`;\n}\n","// `sp auth` — login / logout / status.\n//\n// Login has two browser flows that both end in a long-lived cli_session token saved to\n// ~/.config/simplepush/auth.json (mode 0600):\n//\n// - Localhost-callback (default on a desktop): start a one-shot HTTP server on a\n// random port, open <base-url>/cli/auth/approve?redirect_uri=...&state=..., wait for\n// the browser to redirect the auth code back (a Deferred), exchange it for the token.\n// - Device flow (default over SSH / headless; RFC 8628 style): POST device/start, show\n// a short user_code, the admin approves it in any browser, the CLI polls device/token\n// until the token is minted. No loopback server, so the browser and CLI need not share\n// a machine.\n//\n// The flow is auto-selected (see preferDeviceFlow) and overridable with --device / --web.\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Deferred, Duration, Effect, Exit, Option, Redacted, Schema } from \"effect\";\nimport { createServer } from \"node:http\";\nimport { spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport type { AddressInfo } from \"node:net\";\n\nimport { baseUrlOption, quietOption } from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { AuthStore, InviteStore, VaultStore, bearerToken } from \"../services/stores.js\";\nimport { maskToken } from \"../format.js\";\n\nconst LOGIN_TIMEOUT = Duration.minutes(5);\n\nconst successPage = `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Logged in</title>\n<style>body{font-family:-apple-system,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0;color:#1d1d1f;}\n@media (prefers-color-scheme:dark){body{background:#1c1c1e;color:#f5f5f7;}}</style></head>\n<body><div><h1 style=\"font-weight:600;\">Logged in</h1><p>You can close this tab and return to your terminal.</p></div></body></html>`;\n\nconst errorPage = (msg: string) => `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Error</title></head>\n<body style=\"font-family:-apple-system,sans-serif;padding:2rem;\"><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></body></html>`;\n\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) =>\n ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" })[c] ?? c,\n );\n}\n\n// The server returns the plaintext API key exactly once — on the exchange that\n// bootstrapped it. `lastRotatedAt` is absent (not null) until the key's first\n// rotation — zio-json omits None.\nconst ExchangePayload = Schema.Struct({\n token: Schema.String,\n apiKey: Schema.optional(Schema.NullOr(Schema.String)),\n apiKeyInfo: Schema.Struct({\n prefix: Schema.String,\n createdAt: Schema.String,\n lastRotatedAt: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n});\ntype ExchangePayload = typeof ExchangePayload.Type;\n\nconst DeviceStartResponse = Schema.Struct({\n deviceCode: Schema.String,\n userCode: Schema.String,\n expiresIn: Schema.Number,\n interval: Schema.Number,\n});\n\nconst DeviceTokenError = Schema.Struct({ error: Schema.optional(Schema.String) });\n\nconst decodeJson = <A, I>(schema: Schema.Schema<A, I>) => Schema.decodeUnknown(Schema.parseJson(schema));\n\ninterface CallbackResult {\n readonly code: string;\n readonly state: string;\n}\n\n/** One-shot loopback server: resolves the Deferred with the redirected\n * code+state. Scoped — releasing tears down the server AND its keep-alive\n * sockets (Connection: close + closeAllConnections; without both, the process\n * would hang on the browser's held-open socket). */\nconst startCallbackServer = Effect.gen(function* () {\n const callback = yield* Deferred.make<CallbackResult, UserError>();\n\n const noKeepAlive = { \"Content-Type\": \"text/html; charset=utf-8\", Connection: \"close\" };\n const server = yield* Effect.acquireRelease(\n Effect.sync(() =>\n createServer((req, res) => {\n const path = req.url ?? \"/\";\n if (!path.startsWith(\"/callback\")) {\n res.writeHead(404, { Connection: \"close\" }).end();\n return;\n }\n const url = new URL(path, \"http://localhost\");\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n if (!code || !state) {\n res.writeHead(400, noKeepAlive).end(errorPage(\"Missing code or state.\"));\n Deferred.unsafeDone(callback, Exit.fail(new UserError({ message: \"callback missing code or state\" })));\n return;\n }\n res.writeHead(200, noKeepAlive).end(successPage);\n Deferred.unsafeDone(callback, Exit.succeed({ code, state }));\n }),\n ),\n (server) =>\n Effect.sync(() => {\n server.closeAllConnections();\n server.close();\n }),\n );\n\n const port = yield* Effect.async<number, UserError>((resume) => {\n server.once(\"error\", (e) => resume(Effect.fail(new UserError({ message: `callback server failed: ${e.message}` }))));\n server.listen(0, \"127.0.0.1\", () => resume(Effect.succeed((server.address() as AddressInfo).port)));\n });\n\n return { port, awaitCallback: Deferred.await(callback) } as const;\n});\n\nconst openInBrowser = (url: string) =>\n Effect.sync(() => {\n const cmd =\n process.platform === \"darwin\" ? \"open\"\n : process.platform === \"win32\" ? \"cmd\"\n : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n try {\n const child = spawn(cmd, args, { detached: true, stdio: \"ignore\" });\n child.unref();\n } catch {\n // If spawn fails (rare — `open` etc. missing), the user can copy the URL from stderr.\n }\n });\n\n// Headless / remote machines have no local browser that can receive a localhost\n// callback, so prefer the device flow there. Overridable with --device / --web.\nfunction preferDeviceFlow(): boolean {\n if (process.env.SP_AUTH_DEVICE === \"1\") return true;\n if (process.env.SSH_CONNECTION || process.env.SSH_TTY) return true;\n if (process.platform === \"linux\" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) return true;\n return false;\n}\n\n// Shared tail for both flows: persist the session token and surface the org API key.\nconst finishLogin = (baseUrl: string, payload: ExchangePayload) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const store = yield* AuthStore;\n\n if (!payload.token) return yield* Effect.fail(new UserError({ message: \"exchange returned empty token\" }));\n\n const path = yield* store.save({\n baseUrl,\n token: Redacted.make(payload.token),\n loggedInAt: new Date().toISOString(),\n });\n\n yield* out.info(`saved credentials to ${path}`);\n yield* out.print(\"Logged in.\");\n\n // The plaintext API key appears exactly once, on the bootstrapping\n // exchange. Surface it on stdout so the user can copy it; we do not\n // persist it locally (the CLI authenticates with the session token).\n if (payload.apiKey) {\n yield* out.print(\"\\nOrganization API key (shown once, copy now):\");\n yield* out.print(` ${payload.apiKey}`);\n } else {\n yield* out.print(\n `\\nOrganization API key already provisioned (prefix ${payload.apiKeyInfo.prefix}…).\\n` +\n \"Run `simplepush org api-key rotate` to surface a fresh one.\",\n );\n }\n });\n\nconst runLocalhostFlow = (baseUrl: string) =>\n Effect.scoped(\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n\n const state = randomBytes(16).toString(\"hex\");\n const { port, awaitCallback } = yield* startCallbackServer;\n const redirectUri = `http://127.0.0.1:${port}/callback`;\n const authUrl =\n `${baseUrl}/cli/auth/approve?redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(state)}`;\n\n yield* out.info(`opening browser: ${authUrl}`);\n yield* out.info(\"(if it didn't open, paste that URL into your browser)\");\n yield* openInBrowser(authUrl);\n\n const callback = yield* awaitCallback.pipe(\n Effect.timeoutFail({\n duration: LOGIN_TIMEOUT,\n onTimeout: () => new UserError({ message: `login timed out after ${Duration.toSeconds(LOGIN_TIMEOUT)}s` }),\n }),\n );\n if (callback.state !== state) {\n return yield* Effect.fail(new UserError({ message: \"state mismatch — refusing to exchange (possible CSRF)\" }));\n }\n\n yield* out.info(\"approved, exchanging code...\");\n\n const res = yield* api.unauthedPost(\"exchange\", `${baseUrl}/cli/auth/exchange`, { code: callback.code });\n if (res.status >= 400) {\n return yield* Effect.fail(new UserError({ message: `exchange failed: ${res.status} ${res.body}` }));\n }\n const payload = yield* decodeJson(ExchangePayload)(res.body);\n yield* finishLogin(baseUrl, payload);\n }),\n );\n\nconst runDeviceFlow = (baseUrl: string) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n\n const startRes = yield* api.unauthedPost(\"device start\", `${baseUrl}/cli/auth/device/start`);\n if (startRes.status >= 400) {\n return yield* Effect.fail(new UserError({ message: `device start failed: ${startRes.status} ${startRes.body}` }));\n }\n const start = yield* decodeJson(DeviceStartResponse)(startRes.body);\n\n // The verification page lives on the same host the CLI talks to (mirrors how\n // the localhost flow builds the approve URL), so construct it from baseUrl.\n const verifyUrl = `${baseUrl}/cli/auth/device`;\n const verifyUrlComplete = `${verifyUrl}?user_code=${encodeURIComponent(start.userCode)}`;\n\n yield* out.print(`\\nTo authorize this CLI, open:\\n ${verifyUrl}`);\n yield* out.print(`and enter the code:\\n ${start.userCode}\\n`);\n // On a machine with a browser, open it straight to the pre-filled approve page.\n if (!preferDeviceFlow()) yield* openInBrowser(verifyUrlComplete);\n yield* out.info(\"waiting for approval... (Ctrl-C to cancel)\");\n\n // Recursive poll: sleep, POST, branch on the RFC 8628 error code.\n // `slow_down` stretches the interval; the whole loop races the code expiry.\n const poll = (intervalSeconds: number): Effect.Effect<ExchangePayload, UserError, Api> =>\n Effect.gen(function* () {\n yield* Effect.sleep(Duration.seconds(intervalSeconds));\n const res = yield* (yield* Api).unauthedPost(\"device token\", `${baseUrl}/cli/auth/device/token`, {\n deviceCode: start.deviceCode,\n }).pipe(Effect.mapError((e) => new UserError({ message: `device token exchange failed: ${String(e.cause)}` })));\n if (res.status < 400) {\n return yield* decodeJson(ExchangePayload)(res.body).pipe(\n Effect.mapError(() => new UserError({ message: \"device token exchange returned an unreadable payload\" })),\n );\n }\n const err = yield* decodeJson(DeviceTokenError)(res.body).pipe(Effect.orElseSucceed(() => ({ error: undefined })));\n switch (err.error) {\n case \"authorization_pending\":\n return yield* poll(intervalSeconds);\n case \"slow_down\":\n return yield* poll(intervalSeconds + 5);\n case \"access_denied\":\n return yield* Effect.fail(new UserError({ message: \"authorization was denied\" }));\n case \"expired_token\":\n return yield* Effect.fail(new UserError({ message: \"the code expired — run `sp auth login` again\" }));\n default:\n return yield* Effect.fail(new UserError({ message: `device token exchange failed: ${res.status}` }));\n }\n });\n\n const payload = yield* poll(start.interval > 0 ? start.interval : 5).pipe(\n Effect.timeoutFail({\n duration: Duration.seconds(start.expiresIn),\n onTimeout: () => new UserError({ message: \"device authorization timed out\" }),\n }),\n );\n yield* finishLogin(baseUrl, payload);\n });\n\nconst deviceFlagOption = Options.boolean(\"device\").pipe(\n Options.withDescription(\"Use the device-code flow (for SSH / headless machines). Auto-selected when no local browser is detected.\"),\n);\nconst webFlagOption = Options.boolean(\"web\").pipe(\n Options.withDescription(\"Use the localhost-callback browser flow (default on a desktop with a browser).\"),\n);\n\nconst authLogin = Command.make(\n \"login\",\n {\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n device: deviceFlagOption,\n web: webFlagOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const baseUrl = args[\"base-url\"].replace(/\\/+$/, \"\");\n\n // --device / --web force a flow; otherwise auto-detect (device on SSH/headless).\n const useDevice = args.device || (!args.web && preferDeviceFlow());\n yield* useDevice ? runDeviceFlow(baseUrl) : runLocalhostFlow(baseUrl);\n // No explicit process.exit here: the Effect runtime's teardown exits the\n // process, so undici's keep-alive sockets can't hold the event loop open.\n }),\n);\n\nconst authLogout = Command.make(\"logout\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const auth = yield* AuthStore;\n const removed = yield* auth.clear;\n // Logout also wipes the org-encryption local state — the cached plaintext\n // vault and any issued invite codes — because both are bound to the\n // now-logged-out admin's session. Without this, a subsequent `login` as a\n // different admin would inherit the previous admin's vault cache and\n // invite list.\n yield* (yield* VaultStore).clear;\n yield* (yield* InviteStore).clear;\n // Logout only purges the local file — there is no server-side revocation,\n // so the session itself remains valid on the server.\n yield* out.print(removed ? `Logged out (deleted ${auth.filePath}).` : `No saved credentials at ${auth.filePath}.`);\n }),\n);\n\nconst authStatus = Command.make(\"status\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const store = yield* AuthStore;\n const auth = yield* store.load;\n if (Option.isNone(auth)) {\n yield* out.print(\"Not logged in. Run `sp auth login` to authenticate.\");\n return yield* Effect.fail(new Aborted());\n }\n yield* out.print(\"Logged in.\");\n yield* out.print(` base url: ${auth.value.baseUrl}`);\n yield* out.print(` token: ${maskToken(bearerToken(auth.value))}`);\n yield* out.print(` since: ${auth.value.loggedInAt}`);\n yield* out.print(` file: ${store.filePath}`);\n }),\n);\n\nexport const authCommand = Command.make(\"auth\").pipe(\n Command.withSubcommands([authLogin, authLogout, authStatus]),\n);\n","// The Effect ↔ SDK boundary. `@simplepush/sdk` is promise/async-iterable\n// based; everything crossing that line goes through these three combinators so\n// the rest of the CLI never touches a bare Promise.\n\nimport { Client, OrgClient } from \"@simplepush/sdk\";\nimport { Effect, Scope, Stream } from \"effect\";\n\nimport { SdkFailure } from \"../errors.js\";\n\ntype ClientConfig = ConstructorParameters<typeof Client>[0];\ntype OrgClientConfig = ConstructorParameters<typeof OrgClient>[0];\n\n/** A Client whose websocket/http resources are released with the scope. */\nexport const acquireClient = (config: ClientConfig): Effect.Effect<Client, never, Scope.Scope> =>\n Effect.acquireRelease(\n Effect.sync(() => new Client(config)),\n (client) => Effect.promise(async () => client.close()).pipe(Effect.ignore),\n );\n\n/** An OrgClient (Api-Key or CLI-session bearer) scoped like `acquireClient`. */\nexport const acquireOrgClient = (config: OrgClientConfig): Effect.Effect<OrgClient, never, Scope.Scope> =>\n Effect.acquireRelease(\n Effect.sync(() => new OrgClient(config)),\n (client) => Effect.promise(async () => client.close()).pipe(Effect.ignore),\n );\n\n/** One promise-returning SDK call as a typed Effect. */\nexport const sdkCall = <A>(action: string, f: () => Promise<A>): Effect.Effect<A, SdkFailure> =>\n Effect.tryPromise({ try: f, catch: (cause) => new SdkFailure({ action, cause }) });\n\n/** An SDK async-iterable as a Stream.\n *\n * Termination MUST go through the AbortSignal, never a bare generator\n * `.return()`: the SDK's reconnect backoff only unblocks on signal-abort, so a\n * plain return() deadlocks against the backoff sleep. The iterator wrapper\n * below aborts FIRST (which breaks the sleep), then lets the generator's own\n * cleanup run. The controller is also aborted by the scope finalizer, covering\n * interruption (timeouts, races, Ctrl-C). */\nexport const sdkStream = <A>(\n action: string,\n make: (signal: AbortSignal) => AsyncIterable<A>,\n): Stream.Stream<A, SdkFailure> =>\n Stream.unwrapScoped(\n Effect.map(\n Effect.acquireRelease(\n Effect.sync(() => new AbortController()),\n (controller) => Effect.sync(() => controller.abort()),\n ),\n (controller) =>\n Stream.fromAsyncIterable(\n abortFirst(make(controller.signal), controller),\n (cause) => new SdkFailure({ action, cause }),\n ),\n ),\n );\n\nfunction abortFirst<A>(iterable: AsyncIterable<A>, controller: AbortController): AsyncIterable<A> {\n return {\n [Symbol.asyncIterator]() {\n const it = iterable[Symbol.asyncIterator]();\n return {\n next: () => it.next(),\n return: async () => {\n controller.abort();\n try {\n await it.return?.();\n } catch {\n // The generator surfacing its own abort is expected here.\n }\n return { done: true as const, value: undefined };\n },\n throw: (e?: unknown) =>\n it.throw?.(e) ?? Promise.reject(e instanceof Error ? e : new Error(String(e))),\n };\n },\n };\n}\n","// Where the per-credential broker socket lives. One daemon (one upstream WS)\n// serves every `sp` process sharing the same stream identity — the credential\n// KIND (personal API-Token stream vs org-session stream), the secret, and the\n// base URL — so the path is keyed by a hash of all three, never the secret\n// itself (it must not leak into a world-readable path). Under the user's\n// runtime/temp dir, which is user-owned.\n\nimport { createHash } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** The credential a broker holds — which upstream endpoint it dials and what\n * it authenticates with. `personal` streams `/ws/v1/events` by API-Token; `org`\n * streams `/ws/v1/events/organization` by the CLI-session bearer. Keying the\n * socket by the bearer means a re-login gets a fresh broker (the old session\n * may be revoked); the stale broker idle-exits on its own. */\nexport type BrokerCredential =\n | { kind: \"personal\"; apiToken: string }\n | { kind: \"org\"; bearer: string };\n\n/** Directory for broker sockets. Prefer `$XDG_RUNTIME_DIR` (0700, user-only,\n * tmpfs) when set; otherwise the OS temp dir. */\nexport function daemonDir(): string {\n const xdg = process.env.XDG_RUNTIME_DIR;\n return xdg && xdg.length > 0 ? join(xdg, \"simplepush\") : join(tmpdir(), \"simplepush\");\n}\n\n/** Socket path for a given credential. The hash covers the credential kind,\n * the secret AND the base URL so two accounts, an account vs its org session,\n * or prod vs a local backend never collide on one broker. The kind also\n * appears in the filename for debuggability. */\nexport function daemonSocketPath(credential: BrokerCredential, baseUrl: string): string {\n const secret = credential.kind === \"personal\" ? credential.apiToken : credential.bearer;\n const key = createHash(\"sha256\").update(`${credential.kind}\\n${baseUrl}\\n${secret}`).digest(\"hex\").slice(0, 16);\n return join(daemonDir(), `events-${credential.kind}-${key}.sock`);\n}\n","// The broker: one long-lived process holding a SINGLE upstream events WS\n// connection for a credential — the personal `/ws/v1/events` stream by\n// API-Token, or the org-wide `/ws/v1/events/organization` stream by the CLI\n// session bearer — fanning every event out to the local `sp` processes\n// attached over its Unix socket. Short-lived `sp collect` / `sp events`\n// invocations attach to this instead of each dialing the backend, collapsing N\n// duplicate server-side streams to one.\n//\n// The broker is dumb transport: it forwards RAW (still-encrypted) event frames\n// and holds only the credential (to open the WS). Decryption keys never reach it\n// — each attached CLI decrypts locally. It keeps a bounded ring of recent events\n// so a just-attached client catches the last moments (covers the send→collect\n// gap), maintains the reconnect/resume cursor centrally, and idle-exits once the\n// last client leaves.\n//\n// Structure: a PubSub fans upstream events out to one fiber per attached\n// client; a SubscriptionRef counts clients and drives the debounced idle-exit;\n// the whole broker is one Scope, so socket file, server, client fibers, and\n// the upstream websocket all unwind together whichever of the three racers\n// (upstream end, idle, Ctrl-C interrupt) wins.\n\nimport { createConnection } from \"node:net\";\nimport { FileSystem, Socket } from \"@effect/platform\";\nimport { NodeSocketServer } from \"@effect/platform-node\";\nimport type { Event } from \"@simplepush/sdk\";\nimport { Chunk, Deferred, Duration, Effect, Fiber, Option, PubSub, Ref, Schedule, Stream, SubscriptionRef } from \"effect\";\n\nimport { daemonDir, daemonSocketPath, type BrokerCredential } from \"./paths.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { acquireClient, acquireOrgClient, sdkStream } from \"../services/sdk.js\";\n\nconst RING_MAX = 1000;\nconst IDLE_EXIT = \"60 seconds\";\n// The broker opens its upstream with this much lookback so a broker auto-spawned\n// by the FIRST `sp collect` still backfills a send that just happened — a client\n// attaching with `since = send time` is served from the ring, not missed.\nconst UPSTREAM_LOOKBACK_MS = 10 * 60_000;\n\n/** True if a broker is already listening on `path` (so we don't double-bind). */\nexport const probeDaemon = (path: string, timeoutMs = 500): Effect.Effect<boolean> =>\n Effect.async<boolean>((resume) => {\n const sock = createConnection(path);\n const done = (live: boolean) => {\n sock.destroy();\n resume(Effect.succeed(live));\n };\n const timer = setTimeout(() => done(false), timeoutMs);\n sock.once(\"connect\", () => { clearTimeout(timer); done(true); });\n sock.once(\"error\", () => { clearTimeout(timer); done(false); });\n return Effect.sync(() => { clearTimeout(timer); sock.destroy(); });\n });\n\n/** Wait for a just-spawned broker to come up. */\nexport const waitForDaemon = (path: string, timeout: Duration.DurationInput): Effect.Effect<boolean> =>\n probeDaemon(path, 200).pipe(\n Effect.filterOrFail((up) => up, () => \"not-up\" as const),\n Effect.retry(Schedule.spaced(\"100 millis\")),\n Effect.timeoutOption(timeout),\n Effect.map(Option.isSome),\n Effect.orElseSucceed(() => false),\n );\n\nconst parseSubscribeLine = (line: string): string | undefined => {\n try {\n return (JSON.parse(line) as { since?: string }).since;\n } catch {\n return undefined; // blank / malformed subscribe line → full ring\n }\n};\n\n/** Run the broker for a credential until the upstream dies or it idle-exits.\n * Resolves when the broker has fully shut down. */\nexport const runDaemon = (opts: { credential: BrokerCredential; baseUrl: string }) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const fs = yield* FileSystem.FileSystem;\n const path = daemonSocketPath(opts.credential, opts.baseUrl);\n\n yield* fs.makeDirectory(daemonDir(), { recursive: true }).pipe(Effect.ignore);\n yield* fs.chmod(daemonDir(), 0o700).pipe(Effect.ignore);\n\n // If a live broker already owns this socket, defer to it; a stale socket\n // file (crashed predecessor) is removed so we can bind.\n if (yield* probeDaemon(path)) {\n return yield* out.info(\"a broker is already running for this credential; exiting\");\n }\n yield* fs.remove(path).pipe(Effect.ignore);\n\n const reason = yield* Effect.scoped(\n Effect.gen(function* () {\n const server = yield* NodeSocketServer.make({ path });\n yield* Effect.addFinalizer(() => fs.remove(path).pipe(Effect.ignore));\n yield* fs.chmod(path, 0o600).pipe(Effect.ignore);\n\n const events = yield* PubSub.unbounded<Event>();\n const ring = yield* Ref.make(Chunk.empty<Event>());\n const clients = yield* SubscriptionRef.make(0);\n\n const handleClient = (socket: Socket.Socket) =>\n Effect.scoped(\n Effect.gen(function* () {\n yield* SubscriptionRef.update(clients, (n) => n + 1);\n yield* Effect.addFinalizer(() => SubscriptionRef.update(clients, (n) => n - 1));\n\n const write = yield* socket.writer;\n\n // The client may send one subscribe line `{\"since\":\"…\"}` to request\n // backlog from a point (the send time); anything before the first\n // newline is it. The reader keeps draining afterwards (we ignore\n // the rest) and completes when the peer disconnects.\n const firstLine = yield* Deferred.make<string>();\n const readState = { buf: \"\", done: false };\n const reader = yield* Effect.fork(\n socket.run((data) => {\n if (readState.done) return;\n readState.buf += Buffer.from(data).toString(\"utf8\");\n const nl = readState.buf.indexOf(\"\\n\");\n if (nl === -1) return;\n readState.done = true;\n return Deferred.succeed(firstLine, readState.buf.slice(0, nl));\n }),\n );\n\n // Subscribe BEFORE snapshotting the ring: no event can fall in the\n // gap between backlog and live. An event published between the two\n // shows up in both — `seen` drops that duplicate exactly once.\n const dequeue = yield* PubSub.subscribe(events);\n\n // A client that never sends a subscribe line still goes live after\n // a short grace period, so a bare connection gets the stream.\n const line = yield* Deferred.await(firstLine).pipe(Effect.timeoutOption(\"100 millis\"));\n const since = Option.match(line, { onNone: () => undefined, onSome: parseSubscribeLine });\n\n const snapshot = Chunk.toReadonlyArray(yield* Ref.get(ring));\n const backlog = since === undefined\n ? snapshot\n : snapshot.filter((e) => e.createdAt !== undefined && e.createdAt >= since);\n const seen = new Set<Event>(snapshot);\n for (const ev of backlog) yield* write(JSON.stringify(ev) + \"\\n\");\n\n const live = Stream.fromQueue(dequeue).pipe(\n Stream.filterEffect((ev) => Effect.sync(() => !seen.delete(ev))),\n Stream.runForEach((ev) => write(JSON.stringify(ev) + \"\\n\")),\n );\n\n // Serve until the peer hangs up (reader completes/fails) or a\n // write fails; either way the scope drops the subscription.\n yield* Effect.raceFirst(live, Fiber.join(reader));\n }),\n ).pipe(Effect.catchAllCause(() => Effect.void)); // one bad client never kills the broker\n\n const acceptLoop = server.run(handleClient);\n\n // Idle exit: shut down once the client count has sat at zero for a full\n // idle window (the debounce timer resets on every attach/detach).\n const idleExit = clients.changes.pipe(\n Stream.debounce(IDLE_EXIT),\n Stream.filter((n) => n === 0),\n Stream.take(1),\n Stream.runDrain,\n Effect.as(\"idle, no clients\"),\n );\n\n // The single upstream connection — the flavor matching the credential\n // (`events()` dials the right endpoint with the right auth header on\n // both client classes). Any terminal condition tears the broker down\n // (clients see their socket close and fall back / end).\n const upstream = Effect.scoped(\n Effect.gen(function* () {\n const client =\n opts.credential.kind === \"personal\"\n ? yield* acquireClient({ baseUrl: opts.baseUrl, apiToken: opts.credential.apiToken })\n : yield* acquireOrgClient({ baseUrl: opts.baseUrl, bearerToken: opts.credential.bearer });\n const since = new Date(Date.now() - UPSTREAM_LOOKBACK_MS).toISOString();\n yield* sdkStream(\"upstream events\", (signal) => client.events({ since, signal })).pipe(\n Stream.runForEach((ev) =>\n Ref.update(ring, (r) => {\n const next = Chunk.append(r, ev);\n return Chunk.size(next) > RING_MAX ? Chunk.drop(next, 1) : next;\n }).pipe(Effect.zipRight(PubSub.publish(events, ev))),\n ),\n );\n return \"upstream closed\";\n }),\n ).pipe(\n Effect.catchTag(\"SdkFailure\", (e) =>\n out\n .warn(`upstream events stream failed: ${e.cause instanceof Error ? e.cause.message : String(e.cause)}`)\n .pipe(Effect.as(\"upstream error\")),\n ),\n );\n\n yield* out.info(`broker listening at ${path}`);\n return yield* Effect.raceAll([upstream, idleExit, acceptLoop]);\n }),\n );\n\n yield* out.info(`broker shut down (${reason})`);\n });\n","// Client side of the broker: a `WebSocketFactory` that, instead of dialing the\n// backend, connects to the local broker socket and presents the SDK's\n// `SimplepushWebSocket`. The SDK's stream/hub then read events off the shared\n// broker connection, unchanged — injected through the SDK's\n// `CommonConfig.webSocketFactory` seam.\n//\n// The factory the SDK consumes is necessarily a plain callback (it lives on\n// the far side of the Effect boundary); everything the CLI itself drives —\n// probing, spawning, waiting — is Effect.\n\nimport { connect, type Socket } from \"node:net\";\nimport { spawn } from \"node:child_process\";\nimport type { WebSocketFactory, SimplepushWebSocket } from \"@simplepush/sdk\";\nimport { Effect, Option } from \"effect\";\n\nimport { daemonSocketPath, type BrokerCredential } from \"./paths.js\";\nimport { probeDaemon, waitForDaemon } from \"./server.js\";\nimport { CliOutput } from \"../services/output.js\";\n\n/** Adapt a connected Unix socket carrying newline-delimited event JSON into a\n * `SimplepushWebSocket`. The broker only ever sends us event frames, so\n * `messages()` yields each line verbatim. */\nfunction adaptSocket(socket: Socket, since: string | undefined): SimplepushWebSocket {\n type Frame = { kind: \"msg\"; text: string } | { kind: \"end\" } | { kind: \"error\"; err: Error };\n const queue: Frame[] = [];\n let waiter: ((f: Frame) => void) | null = null;\n const push = (f: Frame) => { if (waiter) { const w = waiter; waiter = null; w(f); } else queue.push(f); };\n\n let closed = false;\n let resolveClosed!: () => void;\n const closedPromise = new Promise<void>((res) => { resolveClosed = res; });\n\n socket.once(\"connect\", () => {\n // Request backlog from the send time so the send→collect gap isn't missed.\n socket.write(JSON.stringify({ since }) + \"\\n\");\n });\n let buf = \"\";\n socket.on(\"data\", (chunk) => {\n buf += chunk.toString(\"utf8\");\n let nl: number;\n while ((nl = buf.indexOf(\"\\n\")) !== -1) {\n const ln = buf.slice(0, nl);\n buf = buf.slice(nl + 1);\n if (ln.trim().length > 0) push({ kind: \"msg\", text: ln });\n }\n });\n socket.on(\"close\", () => { if (!closed) { closed = true; push({ kind: \"end\" }); resolveClosed(); } });\n socket.on(\"error\", (err) => push({ kind: \"error\", err }));\n\n async function* messages(): AsyncIterableIterator<string> {\n while (true) {\n const next = queue.length > 0 ? queue.shift()! : await new Promise<Frame>((res) => { waiter = res; });\n if (next.kind === \"msg\") yield next.text;\n else if (next.kind === \"end\") return;\n else throw next.err;\n }\n }\n\n return { closed: closedPromise, messages, close: () => { try { socket.destroy(); } catch { /* ignore */ } } };\n}\n\n/** A factory that connects to the broker at `path`. The SDK passes the ws url\n * (with `?since=`), which we forward to the broker as the backlog cursor. */\nfunction daemonWebSocketFactory(path: string): WebSocketFactory {\n return (url) => {\n let since: string | undefined;\n try { since = new URL(url).searchParams.get(\"since\") ?? undefined; } catch { /* leave undefined */ }\n return adaptSocket(connect(path), since);\n };\n}\n\n/** Spawn the broker detached; credentials/base-url go via env, NOT argv, so\n * they don't show up in `ps`. Org mode passes the session bearer explicitly\n * (SP_DAEMON_BEARER) rather than letting the child re-read auth.json,\n * so the child's socket path deterministically matches the one we probe. */\nconst spawnDetachedDaemon = (opts: { credential: BrokerCredential; baseUrl: string }) =>\n Effect.sync(() => {\n const credentialEnv =\n opts.credential.kind === \"personal\"\n ? { SP_API_TOKEN: opts.credential.apiToken }\n : { SP_DAEMON_BEARER: opts.credential.bearer };\n const child = spawn(process.execPath, [process.argv[1]!, \"daemon\"], {\n detached: true,\n stdio: \"ignore\",\n env: { ...process.env, ...credentialEnv, SP_BASE_URL: opts.baseUrl },\n });\n child.unref();\n });\n\n/** Resolve the WebSocket transport for a shared-mode client: attach to a running\n * broker, auto-spawning one (detached) if absent. Returns `Option.none` to fall\n * back to a direct connection when the broker can't be reached — the broker is\n * an optimisation and must never break `sp`. */\nexport const sharedWebSocketFactory = (opts: {\n credential: BrokerCredential;\n baseUrl: string;\n}): Effect.Effect<Option.Option<WebSocketFactory>, never, CliOutput> =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const path = daemonSocketPath(opts.credential, opts.baseUrl);\n\n const attach = Effect.gen(function* () {\n if (yield* probeDaemon(path)) {\n yield* out.info(\"attached to the shared events broker\");\n return Option.some(daemonWebSocketFactory(path));\n }\n yield* spawnDetachedDaemon(opts);\n if (!(yield* waitForDaemon(path, \"3 seconds\"))) {\n yield* out.warn(\"could not start the shared events broker; using a direct connection\");\n return Option.none<WebSocketFactory>();\n }\n yield* out.info(\"started shared events broker\");\n return Option.some(daemonWebSocketFactory(path));\n });\n\n return yield* attach.pipe(\n Effect.catchAllCause((cause) =>\n out\n .warn(`shared broker unavailable (${cause.toString()}); using a direct connection`)\n .pipe(Effect.as(Option.none<WebSocketFactory>())),\n ),\n );\n });\n","// `--since` / `--until` parsing: humantime duration (`24h`, `7d`) or ISO 8601.\n\nconst UNIT_MS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n sec: 1_000,\n secs: 1_000,\n m: 60_000,\n min: 60_000,\n mins: 60_000,\n h: 3_600_000,\n hr: 3_600_000,\n hrs: 3_600_000,\n d: 86_400_000,\n day: 86_400_000,\n days: 86_400_000,\n w: 604_800_000,\n wk: 604_800_000,\n wks: 604_800_000,\n};\n\nconst HUMANTIME_RE = /^\\s*(\\d+)\\s*([a-zA-Z]+)\\s*$/;\n\nexport function parseDurationMs(input: string): number | undefined {\n const m = HUMANTIME_RE.exec(input);\n if (!m) return undefined;\n const n = Number(m[1]);\n const unit = m[2]!.toLowerCase();\n const factor = UNIT_MS[unit];\n if (factor === undefined) return undefined;\n return n * factor;\n}\n\nexport function parseIso(input: string): Date | undefined {\n const d = new Date(input);\n return Number.isFinite(d.getTime()) ? d : undefined;\n}\n\n/** ISO 8601 string suitable for the `--since` query param. */\nexport function resolveSince(input: string): string {\n const iso = parseIso(input);\n if (iso) return iso.toISOString();\n const ms = parseDurationMs(input);\n if (ms !== undefined) return new Date(Date.now() - ms).toISOString();\n throw new Error(`could not parse \\`--since ${input}\\`: expected a duration (e.g. \\`24h\\`, \\`7d\\`) or ISO 8601 timestamp`);\n}\n\nexport function resolveUntil(input: string): Date {\n const iso = parseIso(input);\n if (iso) return iso;\n const ms = parseDurationMs(input);\n if (ms !== undefined) return new Date(Date.now() - ms);\n throw new Error(`could not parse \\`--until ${input}\\`: expected a duration or ISO 8601 timestamp`);\n}\n","// The unified, agent-friendly NDJSON envelope for `sp collect`. Every collected\n// item is one self-describing JSON line on stdout, flushed immediately:\n//\n// {\"type\":\"sent\",\"groupId\":…,\"createdAt\":…,\"members\":[{\"taskId\":…,\"recipient\":…}]}\n// {\"type\":\"reply\",\"groupId\":…,\"taskId\":…,\"subtaskId\":null,\"recipient\":{…},\"actor\":{…},\"body\":{…},…}\n// {\"type\":\"input\",\"taskId\":…,\"recipient\":{…},\"actor\":{…},\"inputType\":\"taskInputUploaded\",\"uploads\":[…]}\n// {\"type\":\"completed\",\"taskId\":…,\"recipient\":{…},\"actor\":{…},\"uploads\":[…]}\n// {\"type\":\"deleted\",\"taskId\":…,\"recipient\":{…},\"actor\":{…}}\n// {\"type\":\"submission\",\"id\":…,\"actor\":{…},\"body\":{…},\"photo\":{…},…}\n// {\"type\":\"end\",\"reason\":\"complete\",\"counts\":{…},\"members\":{…}}\n//\n// `recipient` is the ADDRESSING identity (static roster, who the instance was\n// sent to); `actor` is the ATTRIBUTION identity from the wire event (who\n// actually acted: public id, name/device snapshots). They coincide for\n// independent-mode instances; submissions only ever have `actor`. `actor` is\n// null on collective events (e.g. the all-recipients-deleted terminal).\n//\n// A leading `sent` line gives the group + member roster up front; a terminal\n// `end` line (always emitted) tells the agent WHEN and WHY the stream stopped,\n// so it never hangs. `pretty` is a human fallback; `json` (NDJSON) is the\n// agent contract.\n//\n// These are pure line formatters — printing is CliOutput's job.\n\nimport type { Actor, GroupActivity, GroupNotification, Submission } from \"@simplepush/sdk\";\n\nexport type CollectFormat = \"json\" | \"pretty\";\n\nexport type Recipient = { publicId: string; name: string | null };\n/** A roster member: a task instance (`tsk_…`) or a notification instance\n * (`ntf_…`). Serialized on the `sent` line under `taskId` / `notificationId`\n * respectively; internally the prefix-carrying `id` + `kind` pair. */\nexport type Member = { id: string; kind: \"task\" | \"notification\"; recipient: Recipient | null };\n\n/** One collected group item: a task group's activity or a notification\n * group's answer. */\nexport type CollectedItem = GroupActivity | GroupNotification;\n\nexport type EndReason = \"complete\" | \"idle\" | \"count\" | \"timeout\" | \"closed\" | \"error\";\n\n/** Drops circular / non-serialisable internals from SDK view objects: `raw`\n * (the whole wire Event — circular) and `_ctx` (a download context that\n * captures the client). Download methods (`read`/`save`/`downloadUrl` when a\n * function) are omitted by JSON.stringify automatically, leaving file views as\n * their plain metadata (id / contentType / filename / size / checksum). */\nfunction cleanReplacer(key: string, value: unknown): unknown {\n if (key === \"raw\" || key === \"_ctx\") return undefined;\n return value;\n}\n\nfunction line(obj: Record<string, unknown>): string {\n return JSON.stringify(obj, cleanReplacer);\n}\n\nfunction createdAtOf(item: { createdAt?: string; raw?: { createdAt?: string } }): string | undefined {\n return item.createdAt ?? item.raw?.createdAt;\n}\n\n/** Wire-absent → null, and absent inner fields → null, per the envelope's\n * `?? null` convention. */\nfunction actorOf(item: { actor?: Actor }): Record<string, unknown> | null {\n const a = item.actor;\n if (!a) return null;\n return {\n publicId: a.publicId,\n name: a.name ?? null,\n devicePublicId: a.devicePublicId ?? null,\n deviceName: a.deviceName ?? null,\n };\n}\n\nexport function formatSent(groupId: string | undefined, createdAt: string | undefined, members: readonly Member[]): string {\n return line({\n type: \"sent\",\n groupId: groupId ?? null,\n createdAt: createdAt ?? null,\n members: members.map((m) => ({\n ...(m.kind === \"notification\" ? { notificationId: m.id } : { taskId: m.id }),\n recipient: m.recipient,\n })),\n });\n}\n\n/** The member instance's entity id under its kind-specific key. */\nfunction instanceId(instance: CollectedItem[\"instance\"]): Record<string, string | null> {\n const inst = instance as { taskId?: string; notificationId?: string };\n return inst.taskId !== undefined ? { taskId: inst.taskId } : { notificationId: inst.notificationId ?? null };\n}\n\n/** A single collected item — from `replies()`, `inputs()`, the combined\n * `activity()`, or a notification group's answers — into one envelope line,\n * keyed off `item.kind`. Reply, input, terminal completion, and deletion all\n * funnel through here so every collect mode emits the same schema. */\nexport function formatItem(g: CollectedItem, groupId: string | undefined): string {\n const item = g.item;\n const base = { groupId: groupId ?? null, ...instanceId(g.instance), recipient: g.recipient ?? null, actor: actorOf(item), createdAt: createdAtOf(item) ?? null };\n switch (item.kind) {\n case \"reply\":\n return line({\n type: \"reply\", ...base,\n subtaskId: item.subtaskId ?? null, id: item.id ?? null,\n body: item.body ?? null, photo: item.photo ?? null, file: item.file ?? null, audio: item.audio ?? null, location: item.location ?? null,\n });\n case \"input\":\n return line({ type: \"input\", ...base, inputType: item.type, uploads: item.uploads ?? [] });\n case \"taskCompleted\":\n return line({ type: \"completed\", ...base, uploads: item.uploads ?? [] });\n case \"notificationCompleted\":\n // The notification analogue of `completed`: the single answer, with the\n // recipient's reply (text / choice / actions) instead of input uploads.\n return line({ type: \"completed\", ...base, reply: item.reply ?? null });\n case \"taskDeleted\":\n return line({ type: \"deleted\", ...base });\n }\n}\n\nexport function formatSubmission(s: Submission): string {\n return line({\n type: \"submission\",\n id: s.id ?? null,\n actor: actorOf(s),\n body: s.body ?? null,\n photo: s.photo ?? null,\n file: s.file ?? null,\n audio: s.audio ?? null,\n location: s.location ?? null,\n createdAt: s.createdAt ?? null,\n });\n}\n\n// --- file views --------------------------------------------------------------\n\n/** The metadata of a downloadable file riding an item (photo/voice/file upload,\n * reply or submission photo/file/audio). `save` is the SDK-bound download\n * method; `path` is stamped by save-files.ts when `--save-files` is on (the\n * saved location, or null when the download failed). */\nexport type FileView = {\n id?: string;\n contentType?: string;\n checksumSha256?: string;\n size?: number;\n filename?: string;\n path?: string | null;\n save?: (path?: string) => Promise<string>;\n};\n\nconst isFileView = (v: unknown): v is FileView =>\n typeof v === \"object\" && v !== null && typeof (v as { save?: unknown }).save === \"function\";\n\n/** The downloadable file views an item carries: its `uploads` array (photo /\n * voice / file kinds — text/choice/… carry no `save` and drop out) or its\n * reply/submission `photo`/`file`/`audio` fields. Items with neither (deleted\n * markers, notification answers) yield []. */\nexport function fileViewsOf(item: object): FileView[] {\n const o = item as { uploads?: unknown; photo?: unknown; file?: unknown; audio?: unknown };\n const candidates = Array.isArray(o.uploads) ? o.uploads : [o.photo, o.file, o.audio];\n return candidates.filter(isFileView);\n}\n\nexport type MemberStatus = { total: number; completed: number; deleted: number; pending: number };\n\n/** The terminal line: reason the stream stopped, per-type counts, and (for\n * group modes) per-member status. Always the last line on a clean run. */\nexport function formatEnd(reason: EndReason, counts: Record<string, number>, members?: MemberStatus, errorMsg?: string): string {\n const obj: Record<string, unknown> = { type: \"end\", reason, counts };\n if (members) obj.members = members;\n if (errorMsg !== undefined) obj.error = errorMsg;\n return line(obj);\n}\n\n// --- pretty (human) fallback -------------------------------------------------\n\nfunction fmtBytes(n: number): string {\n if (n < 1024) return `${n}B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;\n return `${(n / (1024 * 1024)).toFixed(1)}MB`;\n}\n\n/** `inp_1a2b… cat.jpg 2.1MB -> files/inp_1a2b…-cat.jpg` — the id feeds\n * `sp download`, the path (when saved) locates the local copy. */\nfunction prettyFile(f: FileView): string {\n const parts = [f.id ?? \"?\"];\n if (f.filename) parts.push(f.filename);\n else if (f.contentType) parts.push(f.contentType);\n if (f.size !== undefined) parts.push(fmtBytes(f.size));\n if (f.path) parts.push(`-> ${f.path}`);\n return parts.join(\" \");\n}\n\nfunction prettyFileSuffix(item: object): string {\n const files = fileViewsOf(item);\n return files.length === 0 ? \"\" : ` files: ${files.map(prettyFile).join(\", \")}`;\n}\n\nexport function formatPrettyItem(g: CollectedItem): string {\n const a = g.item.actor;\n const inst = g.instance as { taskId?: string; notificationId?: string };\n const who = a?.name ?? g.recipient?.name ?? a?.publicId ?? g.recipient?.publicId ?? inst.taskId ?? inst.notificationId;\n const item = g.item;\n switch (item.kind) {\n case \"reply\": {\n const text = item.body?.kind === \"text\" ? item.body.text ?? \"\" : JSON.stringify(item.body);\n return ` ${who} (reply): ${text}${prettyFileSuffix(item)}`;\n }\n case \"input\": return ` ${who} [${item.type}]${prettyFileSuffix(item)}`;\n case \"taskCompleted\": return ` ${who} [completed]${prettyFileSuffix(item)}`;\n case \"notificationCompleted\": {\n const r = item.reply;\n const answer = r === undefined ? \"\" : r.type === \"text\" ? `: ${r.value}` : r.type === \"choice\" ? `: ${r.selectedValue}` : `: ${r.selectedKey}`;\n return ` ${who} [answered]${answer}`;\n }\n case \"taskDeleted\": return ` ${who} [deleted]`;\n }\n}\n\nexport function formatPrettySubmission(s: Submission): string {\n const text = s.body?.kind === \"text\" ? s.body.text ?? \"\" : JSON.stringify(s.body);\n const who = s.actor ? s.actor.name ?? s.actor.publicId : undefined;\n const device = s.actor?.deviceName;\n const from = who ? ` from ${who}${device ? ` (${device})` : \"\"}` : \"\";\n return ` submission${from}: ${text}${prettyFileSuffix(s)}`;\n}\n","// Local saving for the downloadable files riding collect items (photo / voice /\n// file uploads, reply files, submission files). The SDK binds `save()` to each\n// file view (checksum-verified, decrypted); this module decides WHERE each file\n// lands and stamps the outcome onto the view itself, so the NDJSON line and the\n// pretty formatter report it: `path` is the saved location, or null when the\n// download failed (the failure itself goes to the caller as a warning).\n//\n// Target names are id-prefixed (`inp_…-cat.jpg`) so files from different\n// recipients never collide, and a re-run overwrites deterministically.\n\nimport { basename, join } from \"node:path\";\n\nimport { fileViewsOf, type FileView } from \"./collect-output.js\";\n\n// Default-filename extensions for the content types the app uploads (mirrors\n// the SDK's map, which it does not export).\nconst EXT: Record<string, string> = {\n \"image/jpeg\": \".jpg\",\n \"image/png\": \".png\",\n \"audio/ogg\": \".ogg\",\n \"audio/wav\": \".wav\",\n \"audio/mp4\": \".m4a\",\n \"video/mp4\": \".mp4\",\n \"application/pdf\": \".pdf\",\n \"application/zip\": \".zip\",\n \"text/plain\": \".txt\",\n};\n\n/** The unique in-directory name for a file view: `<id>-<filename>` when the\n * uploader named it (basename'd — a filename is client-supplied wire data and\n * must not traverse), else `<id>` + a content-type extension. */\nexport function targetName(f: FileView): string {\n const id = f.id ?? \"file\";\n if (f.filename) return `${id}-${basename(f.filename)}`;\n return `${id}${EXT[f.contentType ?? \"\"] ?? \"\"}`;\n}\n\n/** Download every file the item carries into `dir`, stamping each view's\n * `path`. Never throws: per-file failures stamp `path: null` and come back as\n * warning strings. */\nexport async function saveItemFiles(item: object, dir: string): Promise<string[]> {\n const warnings: string[] = [];\n for (const f of fileViewsOf(item)) {\n try {\n f.path = await f.save!(join(dir, targetName(f)));\n } catch (err) {\n f.path = null;\n warnings.push(`could not save ${f.id ?? \"file\"}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n return warnings;\n}\n","// Parse the `sp collect --until` stop conditions. Group collects are bounded\n// by default (they end when every member finishes); the terminal-less modes\n// (--replies, --submissions) default to `forever` — they're watchers, and a\n// surprise 30s exit is worse than an explicit Ctrl-C. Any explicit --until\n// replaces the default.\n//\n// Grammar (repeatable / combinable; the first condition to trip wins):\n// --until complete all group members reached a terminal state (inputs)\n// --until idle:<dur> no event from any member for <dur>\n// --until count:<n> after <n> emitted events\n// --until timeout:<dur> after <dur> of wall-clock, regardless of activity\n// --until forever never stop (Ctrl-C / connection close only); sole condition\n//\n// <dur> is `<number><unit>` with unit ms|s|m|h (e.g. `500ms`, `30s`, `2m`).\n\nexport type UntilConfig = {\n complete: boolean;\n forever?: true;\n idleMs?: number;\n count?: number;\n timeoutMs?: number;\n};\n\nexport function parseDuration(s: string): number {\n const m = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h)$/.exec(s.trim());\n if (!m) throw new Error(`invalid duration \\`${s}\\`: use e.g. 500ms, 30s, 2m, 1h`);\n const n = Number(m[1]);\n const unit = m[2];\n const mult = unit === \"ms\" ? 1 : unit === \"s\" ? 1_000 : unit === \"m\" ? 60_000 : 3_600_000;\n return Math.round(n * mult);\n}\n\nexport function parseUntil(specs: readonly string[]): UntilConfig {\n const cfg: UntilConfig = { complete: false };\n for (const raw of specs) {\n const spec = raw.trim();\n if (spec === \"complete\") {\n cfg.complete = true;\n continue;\n }\n if (spec === \"forever\") {\n cfg.forever = true;\n continue;\n }\n const colon = spec.indexOf(\":\");\n if (colon === -1) {\n throw new Error(`invalid --until \\`${spec}\\`: expected complete | idle:<dur> | count:<n> | timeout:<dur> | forever`);\n }\n const key = spec.slice(0, colon);\n const val = spec.slice(colon + 1);\n switch (key) {\n case \"idle\":\n cfg.idleMs = parseDuration(val);\n break;\n case \"timeout\":\n cfg.timeoutMs = parseDuration(val);\n break;\n case \"count\": {\n const n = Number(val);\n if (!Number.isInteger(n) || n <= 0) throw new Error(`invalid --until count:\\`${val}\\`: expected a positive integer`);\n cfg.count = n;\n break;\n }\n default:\n throw new Error(`invalid --until \\`${spec}\\`: unknown condition \\`${key}\\` (expected idle|count|timeout|complete|forever)`);\n }\n }\n if (cfg.forever && (cfg.complete || cfg.idleMs !== undefined || cfg.count !== undefined || cfg.timeoutMs !== undefined)) {\n throw new Error(\"--until forever cannot be combined with other stop conditions\");\n }\n return cfg;\n}\n\n/** Fill in a sensible default when the caller passed no `--until`: inputs /\n * activity (which have a natural terminal) default to waiting for every member\n * to finish; replies / submissions (no terminal) run `forever` — they're\n * watchers, ended by Ctrl-C or an explicit `--until`. */\nexport function withDefaults(cfg: UntilConfig, mode: \"replies\" | \"inputs\" | \"submissions\" | \"activity\"): UntilConfig {\n if (cfg.forever) return cfg; // explicit opt-out of boundedness: no defaults\n const empty = !cfg.complete && cfg.idleMs === undefined && cfg.count === undefined && cfg.timeoutMs === undefined;\n if (!empty) return cfg;\n // inputs / activity have a natural terminal (every member completing); replies\n // and submissions don't — they watch until explicitly stopped.\n if (mode === \"inputs\" || mode === \"activity\") return { ...cfg, complete: true };\n return { ...cfg, forever: true };\n}\n","// `sp collect` — the agent-friendly collector. Gathers replies or inputs over a\n// task GROUP you sent (or submissions) off the ONE shared `ws/v1/events` stream,\n// and emits the unified NDJSON envelope (see collect-output.ts): a `sent` header,\n// one self-describing line per item (tagged with the recipient), and a terminal\n// `end` line stating WHY it stopped. Always bounded (never hangs).\n//\n// Group context comes from a prior send's `sent` line piped on stdin\n// (`sp task --format json | sp collect`) or from explicit `--group`/`--instance`\n// flags. Passing the send's `createdAt` (carried in the `sent` line) as the\n// resume point means nothing that arrived in the send→collect gap is missed.\n//\n// The stop conditions map onto Stream combinators: count/complete are\n// `takeUntilEffect` (recording WHY in a set-once Ref), wall-clock timeout is\n// `interruptWhen`, submission idle is `timeoutTo`, and group idle rides the\n// SDK's own idleMs (whose end the reason-resolution step names \"idle\").\n\nimport { mkdir } from \"node:fs/promises\";\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Duration, Effect, HashSet, Option, Ref, Schema, Stream } from \"effect\";\nimport type { Client, OrgClient, OrgMasterKey, Submission } from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n DEFAULT_BASE_URL,\n mappedText,\n passwordOption,\n quietOption,\n} from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { acquireClient, acquireOrgClient, sdkStream } from \"../services/sdk.js\";\nimport { AuthStore, bearerToken, VaultStore } from \"../services/stores.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { sharedWebSocketFactory } from \"../daemon/transport.js\";\nimport { resolveSince } from \"../since.js\";\nimport { saveItemFiles } from \"../save-files.js\";\nimport { parseUntil, withDefaults, type UntilConfig } from \"../until.js\";\nimport {\n formatSent,\n formatItem,\n formatSubmission,\n formatEnd,\n formatPrettyItem,\n formatPrettySubmission,\n type CollectedItem,\n type Member,\n type EndReason,\n type MemberStatus,\n} from \"../collect-output.js\";\n\n// The piped `sent` line from a prior `sp task --format json` (task members)\n// or `sp notify --format json` (notification members).\nconst SentLine = Schema.Struct({\n type: Schema.Literal(\"sent\"),\n groupId: Schema.optional(Schema.NullOr(Schema.String)),\n createdAt: Schema.optional(Schema.NullOr(Schema.String)),\n members: Schema.optional(\n Schema.Array(\n Schema.Struct({\n taskId: Schema.optional(Schema.String),\n notificationId: Schema.optional(Schema.String),\n recipient: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n publicId: Schema.String,\n name: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n }),\n ),\n ),\n});\ntype SentLine = typeof SentLine.Type;\n\nconst parseSentLine = Schema.decodeUnknownOption(Schema.parseJson(SentLine));\n\n/** Read the first `{\"type\":\"sent\",…}` line off stdin (when piped), returning as\n * soon as it's found — it never blocks waiting for the producer to close. */\nconst readSentLine: Effect.Effect<Option.Option<SentLine>> = Effect.suspend(() => {\n if (process.stdin.isTTY) return Effect.succeedNone;\n return Stream.fromAsyncIterable(process.stdin as AsyncIterable<Buffer>, (e) => e).pipe(\n Stream.map((chunk) => chunk.toString(\"utf8\")),\n Stream.splitLines,\n Stream.filterMap((line) => parseSentLine(line.trim())),\n Stream.runHead,\n Effect.orElseSucceed(() => Option.none<SentLine>()),\n Effect.ensuring(\n // Don't let an open stdin keep the process alive during collection.\n Effect.sync(() => {\n try { (process.stdin as unknown as { unref?: () => void }).unref?.(); } catch { /* noop */ }\n }),\n ),\n );\n});\n\ntype Mode = \"replies\" | \"inputs\" | \"submissions\" | \"activity\";\n\n// ---- credential resolution ---------------------------------------------\n\ntype Credential =\n | { kind: \"personal\"; apiToken: string; baseUrl: string }\n | { kind: \"org\"; bearer: string; baseUrl: string };\n\n/** Personal mode when `--api-token` (or $SP_API_TOKEN) is given;\n * otherwise fall back to the saved CLI session (org mode) — the same session\n * that powers org sends, so a logged-in admin collects without extra\n * credentials. Org mode follows the session's base URL; an explicitly\n * different `--base-url` is a conflict, not a silent override. */\nexport const resolveCredential = (\n tokenOpt: Option.Option<string>,\n baseUrlArg: string,\n): Effect.Effect<Credential, UserError, AuthStore> =>\n Effect.gen(function* () {\n if (Option.isSome(tokenOpt)) return { kind: \"personal\", apiToken: tokenOpt.value, baseUrl: baseUrlArg } as const;\n const store = yield* AuthStore;\n // An unreadable/corrupt auth.json behaves like \"not logged in\".\n const auth = yield* store.load.pipe(Effect.orElseSucceed(Option.none));\n if (Option.isNone(auth)) {\n return yield* Effect.fail(\n new UserError({\n message:\n \"no credential: pass --api-token (or set $SP_API_TOKEN) to collect your personal stream, or `sp auth login` to collect your organization's\",\n }),\n );\n }\n const session = auth.value;\n if (baseUrlArg !== DEFAULT_BASE_URL && baseUrlArg !== session.baseUrl) {\n return yield* Effect.fail(\n new UserError({\n message: `the CLI session is for ${session.baseUrl}, not ${baseUrlArg} — log in there, or pass --api-token to collect a personal stream instead`,\n }),\n );\n }\n return { kind: \"org\", bearer: bearerToken(session), baseUrl: session.baseUrl } as const;\n });\n\n/** Org master keys for decryption: the cached vault when present; a missing\n * cache prompts for the passphrase on a TTY (same UX as an encrypted org\n * send). Piped runs never prompt — stdin belongs to the `sent` line — and\n * proceed keyless with a warning (encrypted content passes through\n * undecrypted). `EncryptionDisabled` means keyless is simply correct. */\nexport const loadOrgMasterKeys = Effect.gen(function* () {\n const out = yield* CliOutput;\n const cached = yield* (yield* VaultStore).load.pipe(Effect.orElseSucceed(Option.none));\n const vault = Option.isSome(cached)\n ? cached.value\n : process.stdin.isTTY\n ? yield* (yield* VaultAccess).getOrPrompt.pipe(Effect.catchTag(\"EncryptionDisabled\", () => Effect.succeed(undefined)))\n : undefined;\n if (vault === undefined) {\n if (Option.isNone(cached) && !process.stdin.isTTY) {\n yield* out.warn(\"org vault is locked on this machine — encrypted org content will not decrypt (run `sp collect` once on a TTY to unlock)\");\n }\n return undefined;\n }\n const keys: OrgMasterKey[] = [vault.masterKeyCurrent, ...vault.masterKeyHistory].map((k) => ({ version: k.version, key: k.key }));\n return keys;\n});\n\nexport const collectCommand = Command.make(\n \"collect\",\n {\n group: Options.text(\"group\").pipe(\n Options.withDescription(\"Group id (grptsk_…) to collect over. Usually supplied via the piped `sent` line instead.\"),\n Options.optional,\n ),\n instance: Options.text(\"instance\").pipe(\n Options.withDescription(\"Member instance id to collect: a task (tsk_…) or a notification (ntf_…). Repeatable. Augments/overrides the piped `sent` line's members.\"),\n Options.repeated,\n ),\n replies: Options.boolean(\"replies\").pipe(Options.withDescription(\"Collect only replies. Default (no mode flag) is the full activity stream: inputs, replies, and completions.\")),\n inputs: Options.boolean(\"inputs\").pipe(Options.withDescription(\"Collect only input events (waits for every member to complete by default).\")),\n submissions: Options.boolean(\"submissions\").pipe(Options.withDescription(\"Collect submissions (your inbox) instead of a group's events.\")),\n since: mappedText(\"since\", resolveSince).pipe(\n Options.withDescription(\"Resume point (`24h`, `7d`, or ISO 8601). Backfills group events or submissions from that point; defaults to the send's createdAt from the piped `sent` line. Implies --direct (the broker can't serve a deep backfill).\"),\n Options.optional,\n ),\n until: Options.text(\"until\").pipe(\n Options.withDescription(\"Stop condition. Repeatable: complete | idle:<dur> | count:<n> | timeout:<dur> | forever (never stop; Ctrl-C to end). Default: complete for group collects; --replies / --submissions watch forever.\"),\n Options.repeated,\n ),\n format: Options.choice(\"format\", [\"json\", \"pretty\"] as const).pipe(\n Options.withDescription(\"Output format: json (NDJSON, agent contract) or pretty (human).\"),\n Options.withDefault(\"json\"),\n ),\n direct: Options.boolean(\"direct\").pipe(\n Options.withDescription(\"Open an independent WS connection. By default sp shares ONE broker connection across all processes (auto-started); --direct bypasses it.\"),\n ),\n \"save-files\": Options.text(\"save-files\").pipe(\n Options.withDescription(\"Download every collected file (photo/voice/file uploads, reply and submission files) into this directory as it streams in, decrypted and checksum-verified. Each file object on the emitted line gains `path`: the saved location, or null when its download failed.\"),\n Options.optional,\n ),\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const cred = yield* resolveCredential(args[\"api-token\"], args[\"base-url\"]);\n const baseUrl = cred.baseUrl;\n const sinceIso = Option.getOrUndefined(args.since);\n\n const mode: Mode =\n args.submissions ? \"submissions\"\n : args.inputs && args.replies ? \"activity\" // both → the combined inputs+replies stream\n : args.inputs ? \"inputs\"\n : args.replies ? \"replies\"\n : \"activity\"; // no mode flag → everything the group produces (inputs, replies, completions)\n\n const until = withDefaults(\n yield* Effect.try({\n try: () => parseUntil(args.until),\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n }),\n mode,\n );\n\n // Default: route the event stream through the local broker (ONE shared WS\n // for all sp processes on the same credential — personal AND org alike);\n // `--direct` opts out. An explicit `--since` also goes direct: the broker\n // only holds a recent window (its ring), so it can't serve a deep\n // backfill — same rule as `sp events`. The broker is best-effort —\n // sharedWebSocketFactory falls back to a direct connection on any failure.\n const webSocketFactory = args.direct || sinceIso !== undefined\n ? Option.none()\n : yield* sharedWebSocketFactory({\n credential:\n cred.kind === \"personal\"\n ? { kind: \"personal\", apiToken: cred.apiToken }\n : { kind: \"org\", bearer: cred.bearer },\n baseUrl,\n });\n const factoryConfig = Option.match(webSocketFactory, { onNone: () => ({}), onSome: (f) => ({ webSocketFactory: f }) });\n\n const orgKeys = cred.kind === \"org\" ? yield* loadOrgMasterKeys : undefined;\n if (cred.kind === \"org\") yield* out.info(`collecting via org session (${baseUrl})`);\n\n const saveDir = Option.getOrUndefined(args[\"save-files\"]);\n if (saveDir !== undefined) {\n yield* Effect.tryPromise({\n try: () => mkdir(saveDir, { recursive: true }),\n catch: (e) => new UserError({ message: `cannot create --save-files directory ${saveDir}: ${e instanceof Error ? e.message : String(e)}` }),\n });\n }\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client =\n cred.kind === \"personal\"\n ? yield* acquireClient({\n baseUrl,\n apiToken: cred.apiToken,\n passwords: [...args.password],\n ...factoryConfig,\n })\n : yield* acquireOrgClient({\n baseUrl,\n bearerToken: cred.bearer,\n ...(orgKeys !== undefined ? { orgMasterKeys: orgKeys } : {}),\n ...factoryConfig,\n });\n\n if (mode === \"submissions\") return yield* collectSubmissions(client, args.format, until, sinceIso, saveDir);\n\n // Group modes: assemble the member roster from the piped `sent` line\n // and/or explicit --instance ids.\n const sent = Option.getOrUndefined(yield* readSentLine);\n const groupId = Option.getOrUndefined(args.group) ?? sent?.groupId ?? undefined;\n const createdAt = sinceIso ?? sent?.createdAt ?? undefined;\n\n const members: Member[] = [];\n const seen = new Set<string>();\n const addMember = (id: string | undefined, recipient: Member[\"recipient\"]) => {\n if (id && !seen.has(id)) {\n seen.add(id);\n members.push({ id, kind: id.startsWith(\"ntf_\") ? \"notification\" : \"task\", recipient });\n }\n };\n for (const m of sent?.members ?? []) {\n addMember(\n m.taskId ?? m.notificationId,\n m.recipient ? { publicId: m.recipient.publicId, name: m.recipient.name ?? null } : null,\n );\n }\n for (const id of args.instance) addMember(id, null);\n\n if (members.length === 0) {\n return yield* Effect.fail(\n new UserError({\n message: \"no members to collect: pipe a send's `sent` line (`sp task --format json | sp collect`) or pass --instance <tsk_…|ntf_…> (repeatable)\",\n }),\n );\n }\n const notifRoster = members[0]!.kind === \"notification\";\n if (members.some((m) => (m.kind === \"notification\") !== notifRoster)) {\n return yield* Effect.fail(\n new UserError({ message: \"cannot mix task (tsk_…) and notification (ntf_…) members in one collect — run one per kind\" }),\n );\n }\n\n // Group idle rides the SDK's own idleMs (its group streams end\n // cleanly on idle); collectGroup's reason resolution names that end\n // \"idle\".\n const streamOpts = { replay: true, ...(until.idleMs !== undefined ? { idleMs: until.idleMs } : {}) };\n const watchGroupId = groupId ?? members[0]!.id; // synthetic id for a single-instance collect\n\n // One stream per roster kind + mode; all yield `{instance, item,\n // recipient}`, so a single tap + `formatItem` (keyed on item.kind)\n // serves replies, inputs, activity, and notification answers\n // uniformly.\n let source: (signal: AbortSignal) => AsyncIterableIterator<CollectedItem>;\n if (notifRoster) {\n // Notifications carry no reply composer; their whole activity IS\n // the single answer, so every non-replies mode maps to `inputs()`.\n if (mode === \"replies\") {\n return yield* Effect.fail(\n new UserError({ message: \"notifications have no replies — collect their answers with --inputs or no mode flag\" }),\n );\n }\n const group = client.watchNotificationGroup({\n groupId: watchGroupId,\n ...(createdAt ? { createdAt } : {}),\n members: members.map((m) => (m.recipient ? { notificationId: m.id, recipient: m.recipient } : { notificationId: m.id })),\n });\n source = (signal) => group.inputs({ ...streamOpts, signal });\n } else {\n const group = client.watchTaskGroup({\n groupId: watchGroupId,\n ...(createdAt ? { createdAt } : {}),\n members: members.map((m) => (m.recipient ? { taskId: m.id, recipient: m.recipient } : { taskId: m.id })),\n });\n source = (signal) =>\n mode === \"inputs\" ? group.inputs({ ...streamOpts, signal })\n : mode === \"replies\" ? group.replies({ ...streamOpts, signal })\n : group.activity({ ...streamOpts, signal });\n }\n\n if (args.format === \"json\") yield* out.print(formatSent(groupId, createdAt, members));\n else yield* out.info(`collecting ${mode} over ${members.length} member(s)${groupId ? ` of ${groupId}` : \"\"}`);\n\n yield* collectGroup(watchGroupId, source, members, args.format, until, saveDir);\n }),\n );\n }),\n);\n\n/** A set-once end-reason cell: the first condition to trip names the reason. */\nconst makeReason = Effect.map(Ref.make(Option.none<EndReason>()), (ref) => ({\n set: (r: EndReason) => Ref.update(ref, Option.orElse(() => Option.some(r))),\n get: Ref.get(ref),\n}));\n\n/** Download the item's files into `dir` (stamping each view's `path`) before\n * its line is emitted; failures warn on stderr and stamp `path: null`. */\nconst saveFiles = (item: Parameters<typeof saveItemFiles>[0], dir: string | undefined) =>\n dir === undefined\n ? Effect.void\n : Effect.gen(function* () {\n const out = yield* CliOutput;\n const warnings = yield* Effect.promise(() => saveItemFiles(item, dir));\n for (const w of warnings) yield* out.warn(w);\n });\n\nconst collectSubmissions = (client: Client | OrgClient, format: \"json\" | \"pretty\", until: UntilConfig, since?: string, saveDir?: string) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const reason = yield* makeReason;\n const counts = yield* Ref.make(0);\n\n // Idle/timeout are enforced HERE (not delegated to the SDK's idle option):\n // ending the stream interrupts the scope, whose finalizer aborts the SDK\n // generator via its signal — that breaks the reconnect backoff, so\n // termination is prompt even on a flapping connection.\n const failed = yield* sdkStream(\"submissions stream\", (signal) =>\n client.submissions({ signal, ...(since !== undefined ? { since } : {}) }),\n ).pipe(\n until.idleMs !== undefined\n ? Stream.timeoutTo(Duration.millis(until.idleMs), Stream.drain(Stream.fromEffect(reason.set(\"idle\"))))\n : (s) => s,\n Stream.tap((s: Submission) =>\n saveFiles(s, saveDir).pipe(\n Effect.zipRight(out.print(format === \"json\" ? formatSubmission(s) : formatPrettySubmission(s))),\n Effect.zipRight(Ref.update(counts, (n) => n + 1)),\n ),\n ),\n until.count !== undefined\n ? Stream.takeUntilEffect(() =>\n Effect.gen(function* () {\n if ((yield* Ref.get(counts)) < until.count!) return false;\n yield* reason.set(\"count\");\n return true;\n }),\n )\n : (s) => s,\n until.timeoutMs !== undefined\n ? Stream.interruptWhen(Effect.sleep(Duration.millis(until.timeoutMs)).pipe(Effect.zipRight(reason.set(\"timeout\"))))\n : (s) => s,\n Stream.runDrain,\n Effect.matchEffect({\n onFailure: (e) =>\n reason.set(\"error\").pipe(\n Effect.zipRight(out.error(`submissions stream failed: ${e.cause instanceof Error ? e.cause.message : String(e.cause)}`)),\n Effect.as(true),\n ),\n onSuccess: () => Effect.succeed(false),\n }),\n );\n\n const total = yield* Ref.get(counts);\n const why = Option.getOrElse(yield* reason.get, (): EndReason => \"closed\");\n if (format === \"json\") {\n yield* out.print(formatEnd(why, total > 0 ? { submission: total } : {}, undefined, why === \"error\" ? \"submissions stream failed\" : undefined));\n } else {\n yield* out.info(`done (${why}): ${JSON.stringify(total > 0 ? { submission: total } : {})}`);\n }\n if (failed) return yield* Effect.fail(new Aborted());\n });\n\nconst collectGroup = (\n groupId: string,\n source: (signal: AbortSignal) => AsyncIterableIterator<CollectedItem>,\n members: readonly Member[],\n format: \"json\" | \"pretty\",\n until: UntilConfig,\n saveDir?: string,\n) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const reason = yield* makeReason;\n const counts = yield* Ref.make<Record<string, number>>({});\n const completed = yield* Ref.make(HashSet.empty<string>());\n const deleted = yield* Ref.make(HashSet.empty<string>());\n const total = yield* Ref.make(0);\n\n const doneCount = Effect.gen(function* () {\n return HashSet.size(yield* Ref.get(completed)) + HashSet.size(yield* Ref.get(deleted));\n });\n\n const instanceIdOf = (g: CollectedItem): string => {\n const inst = g.instance as { taskId?: string; notificationId?: string };\n return inst.taskId ?? inst.notificationId ?? \"\";\n };\n\n const failed = yield* sdkStream(\"collect stream\", source).pipe(\n Stream.tap((g: CollectedItem) =>\n Effect.gen(function* () {\n yield* saveFiles(g.item, saveDir);\n yield* out.print(format === \"json\" ? formatItem(g, groupId) : formatPrettyItem(g));\n yield* Ref.update(total, (n) => n + 1);\n const kind = g.item.kind;\n const label = kind === \"reply\" ? \"reply\" : kind === \"input\" ? \"input\" : kind === \"taskDeleted\" ? \"deleted\" : \"completed\";\n yield* Ref.update(counts, (c) => ({ ...c, [label]: (c[label] ?? 0) + 1 }));\n if (kind === \"taskCompleted\" || kind === \"notificationCompleted\") yield* Ref.update(completed, HashSet.add(instanceIdOf(g)));\n if (kind === \"taskDeleted\") yield* Ref.update(deleted, HashSet.add(instanceIdOf(g)));\n }),\n ),\n until.count !== undefined\n ? Stream.takeUntilEffect(() =>\n Effect.gen(function* () {\n if ((yield* Ref.get(total)) < until.count!) return false;\n yield* reason.set(\"count\");\n return true;\n }),\n )\n : (s) => s,\n // activity() has no natural terminal, so enforce \"every member finished\"\n // here (a no-op for replies, redundant-but-harmless for inputs).\n until.complete && members.length > 0\n ? Stream.takeUntilEffect(() =>\n Effect.gen(function* () {\n if ((yield* doneCount) < members.length) return false;\n yield* reason.set(\"complete\");\n return true;\n }),\n )\n : (s) => s,\n until.timeoutMs !== undefined\n ? Stream.interruptWhen(Effect.sleep(Duration.millis(until.timeoutMs)).pipe(Effect.zipRight(reason.set(\"timeout\"))))\n : (s) => s,\n Stream.runDrain,\n Effect.matchEffect({\n onFailure: (e) =>\n reason.set(\"error\").pipe(\n Effect.zipRight(out.error(`collect stream failed: ${e.cause instanceof Error ? e.cause.message : String(e.cause)}`)),\n Effect.as(true),\n ),\n onSuccess: () => Effect.succeed(false),\n }),\n );\n\n // No explicit trip → the stream ended on its own: everyone finished, the\n // SDK idle window elapsed, or the connection closed.\n const done = yield* doneCount;\n const fallback: EndReason =\n members.length > 0 && done >= members.length ? \"complete\" : until.idleMs !== undefined ? \"idle\" : \"closed\";\n const why = Option.getOrElse(yield* reason.get, () => fallback);\n\n const memberStatus: MemberStatus = {\n total: members.length,\n completed: HashSet.size(yield* Ref.get(completed)),\n deleted: HashSet.size(yield* Ref.get(deleted)),\n pending: members.length - done,\n };\n if (format === \"json\") {\n yield* out.print(formatEnd(why, yield* Ref.get(counts), memberStatus, why === \"error\" ? \"collect stream failed\" : undefined));\n } else {\n yield* out.info(`done (${why}): ${JSON.stringify(yield* Ref.get(counts))}`);\n }\n if (failed) return yield* Effect.fail(new Aborted());\n });\n","// `sp daemon` — run the shared events broker in the foreground. Normally started\n// automatically by `sp collect` / `sp events` (detached), but exposed so it can\n// be run/inspected directly. Blocks until the upstream stream ends or it\n// idle-exits once the last client leaves.\n//\n// Credential precedence mirrors `sp collect`: an explicit --api-token (or\n// $SP_API_TOKEN) runs a PERSONAL broker; otherwise\n// $SP_DAEMON_BEARER (set by the auto-spawn, so the child's socket path\n// deterministically matches the parent's probe) or the saved CLI session runs\n// an ORG broker.\n\nimport { Command } from \"@effect/cli\";\nimport { Effect, Option } from \"effect\";\n\nimport { apiTokenOption, baseUrlOption, quietOption } from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { AuthStore, bearerToken } from \"../services/stores.js\";\nimport { runDaemon } from \"../daemon/server.js\";\nimport type { BrokerCredential } from \"../daemon/paths.js\";\n\nconst resolveDaemonCredential = (\n tokenOpt: Option.Option<string>,\n): Effect.Effect<BrokerCredential, UserError, AuthStore> =>\n Effect.gen(function* () {\n if (Option.isSome(tokenOpt)) return { kind: \"personal\", apiToken: tokenOpt.value } as const;\n const spawnedBearer = process.env.SP_DAEMON_BEARER;\n if (spawnedBearer) return { kind: \"org\", bearer: spawnedBearer } as const;\n const store = yield* AuthStore;\n const auth = yield* store.load.pipe(Effect.orElseSucceed(Option.none));\n if (Option.isSome(auth)) return { kind: \"org\", bearer: bearerToken(auth.value) } as const;\n return yield* Effect.fail(\n new UserError({\n message:\n \"no credential: pass --api-token (or set $SP_API_TOKEN) for a personal broker, or `sp auth login` for an org broker\",\n }),\n );\n });\n\nexport const daemonCommand = Command.make(\n \"daemon\",\n {\n \"api-token\": apiTokenOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const credential = yield* resolveDaemonCredential(args[\"api-token\"]);\n yield* runDaemon({ credential, baseUrl: args[\"base-url\"] });\n }),\n);\n","// `sp download` — fetch one file a collect line referenced: a task input\n// upload (inp_…), a reply file (rfl_…), or a submission file (sbf_…), by the\n// ids the NDJSON envelope carries.\n//\n// The presign endpoints return only a URL; a file's encryption marker,\n// checksum, and filename live on the EVENT that announced it. So the command\n// replays the scope's event stream (the same machinery `sp collect` uses),\n// finds the file's view, and calls its SDK-bound `save()` — checksum-verified\n// and decrypted with this client's keys. The replay window defaults to 7d;\n// pass --since for older files. Always a direct WS connection (the broker's\n// ring can't serve a deep backfill).\n\nimport { Args, Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Stream } from \"effect\";\nimport type { Submission } from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n} from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { acquireClient, acquireOrgClient, sdkStream } from \"../services/sdk.js\";\nimport { resolveSince } from \"../since.js\";\nimport { fileViewsOf, type FileView } from \"../collect-output.js\";\nimport { loadOrgMasterKeys, resolveCredential } from \"./collect.js\";\n\n// Silence window that ends the replay: once the backfill has caught up,\n// nothing more is coming for an already-collected file.\nconst IDLE_MS = 10_000;\n\ntype ScopeKind = \"task\" | \"submission\";\n\nconst resolveScope = (scopeId: string, fileId: string): Effect.Effect<ScopeKind, UserError> => {\n if (scopeId.startsWith(\"sub_\")) {\n return Effect.fail(\n new UserError({ message: \"subtask files are addressed by their parent task: pass the tsk_… id (collect reply/input lines carry it as taskId)\" }),\n );\n }\n const kind: ScopeKind | undefined = scopeId.startsWith(\"tsk_\") ? \"task\" : scopeId.startsWith(\"sbm_\") ? \"submission\" : undefined;\n if (kind === undefined) {\n return Effect.fail(new UserError({ message: `expected a task (tsk_…) or submission (sbm_…) scope id, got '${scopeId}'` }));\n }\n const wantedScope: ScopeKind | undefined =\n fileId.startsWith(\"inp_\") || fileId.startsWith(\"rfl_\") ? \"task\" : fileId.startsWith(\"sbf_\") ? \"submission\" : undefined;\n if (wantedScope === undefined) {\n return Effect.fail(new UserError({ message: `expected an input upload (inp_…), reply file (rfl_…), or submission file (sbf_…) id, got '${fileId}'` }));\n }\n if (wantedScope !== kind) {\n return Effect.fail(\n new UserError({\n message:\n wantedScope === \"task\"\n ? `${fileId} is a task-scoped file — pass its tsk_… id, not ${scopeId}`\n : `${fileId} is a submission file — pass its sbm_… id, not ${scopeId}`,\n }),\n );\n }\n return Effect.succeed(kind);\n};\n\nexport const downloadCommand = Command.make(\n \"download\",\n {\n scopeId: Args.text({ name: \"scope-id\" }).pipe(\n Args.withDescription(\"The containing entity: a task (tsk_…, the `taskId` on collect reply/input lines) or a submission (sbm_…, the `id` on submission lines).\"),\n ),\n fileId: Args.text({ name: \"file-id\" }).pipe(\n Args.withDescription(\"The file to download: an input upload (inp_…), reply file (rfl_…), or submission file (sbf_…) — the `id` on the line's file object.\"),\n ),\n out: Options.text(\"out\").pipe(\n Options.withDescription(\"Where to save: a file path, an existing directory (the upload's filename is used inside it), or omitted for the current directory.\"),\n Options.optional,\n ),\n since: Options.text(\"since\").pipe(\n Options.withDescription(\"How far back to search the event stream for the file (`24h`, `90d`, or ISO 8601). Default: 7d.\"),\n Options.withDefault(\"7d\"),\n ),\n format: Options.choice(\"format\", [\"json\", \"pretty\"] as const).pipe(\n Options.withDescription(\"Output format: json (one `downloaded` line) or pretty (the saved path).\"),\n Options.withDefault(\"json\"),\n ),\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const scope = yield* resolveScope(args.scopeId, args.fileId);\n const sinceIso = resolveSince(args.since);\n const cred = yield* resolveCredential(args[\"api-token\"], args[\"base-url\"]);\n const orgKeys = cred.kind === \"org\" ? yield* loadOrgMasterKeys : undefined;\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n // Connectivity bookkeeping, so an empty search can name its real\n // cause: a stream that never connected (bad host, server down —\n // silently retried by the SDK until our idle window ends) reads very\n // differently from a healthy replay that simply lacked the file.\n const health = { reconnects: 0, lastError: undefined as Error | undefined, eventsSeen: 0 };\n const onReconnect = (_attempt: number, _backoffMs: number, lastError: Error | undefined) => {\n health.reconnects += 1;\n if (lastError) health.lastError = lastError;\n };\n\n const client =\n cred.kind === \"personal\"\n ? yield* acquireClient({ baseUrl: cred.baseUrl, apiToken: cred.apiToken, passwords: [...args.password], onReconnect })\n : yield* acquireOrgClient({ baseUrl: cred.baseUrl, bearerToken: cred.bearer, ...(orgKeys !== undefined ? { orgMasterKeys: orgKeys } : {}), onReconnect });\n\n const matching = (item: Parameters<typeof fileViewsOf>[0]) =>\n Option.fromNullable(fileViewsOf(item).find((f) => f.id === args.fileId));\n\n const countSeen = <A>(s: Stream.Stream<A, { cause: unknown }>) =>\n s.pipe(Stream.tap(() => Effect.sync(() => { health.eventsSeen += 1; })));\n\n const fileStream: Stream.Stream<FileView, { cause: unknown }> =\n scope === \"task\"\n ? countSeen(\n sdkStream(\"task activity\", (signal) =>\n client\n .watchTaskGroup({ groupId: args.scopeId, createdAt: sinceIso, members: [{ taskId: args.scopeId }] })\n .activity({ replay: true, idleMs: IDLE_MS, signal }),\n ),\n ).pipe(Stream.filterMap((g) => matching(g.item)))\n : countSeen(sdkStream(\"submissions stream\", (signal) => client.submissions({ signal, since: sinceIso, idleMs: IDLE_MS }))).pipe(\n Stream.filterMap((s: Submission) => (s.id === args.scopeId ? matching(s) : Option.none())),\n );\n\n // A permanent stream failure (e.g. a 401 handshake: wrong credentials\n // for this --base-url) is thrown by the SDK and surfaces here.\n const found = yield* Stream.runHead(fileStream).pipe(\n Effect.mapError(\n (e) =>\n new UserError({\n message: `event stream failed: ${e.cause instanceof Error ? e.cause.message : String(e.cause)} — check --base-url and the credential`,\n }),\n ),\n );\n if (Option.isNone(found)) {\n const message =\n health.lastError !== undefined\n ? `could not read the event stream (${health.lastError.message}) — check --base-url and the credential`\n : health.eventsSeen === 0\n ? `no events for ${args.scopeId} arrived since ${args.since} — check the ids and --base-url, or widen the search with --since (e.g. --since 90d)`\n : `${args.fileId} not seen on ${args.scopeId} since ${args.since} (${health.eventsSeen} events replayed) — check the file id, or widen the search with --since (e.g. --since 90d)`;\n return yield* Effect.fail(new UserError({ message }));\n }\n const file = found.value;\n\n const path = yield* Effect.tryPromise({\n try: () => file.save!(Option.getOrUndefined(args.out)),\n catch: (e) => new UserError({ message: `download failed: ${e instanceof Error ? e.message : String(e)}` }),\n });\n\n if (args.format === \"json\") {\n yield* out.print(\n JSON.stringify({\n type: \"downloaded\",\n id: file.id ?? null,\n path,\n filename: file.filename ?? null,\n contentType: file.contentType ?? null,\n size: file.size ?? null,\n }),\n );\n } else {\n yield* out.print(path);\n }\n }),\n );\n }),\n);\n","// Format events according to the user-selected `--format`. Pure: returns the\n// stdout line (sans trailing newline); printing is CliOutput's job.\n\nimport type { Event } from \"@simplepush/sdk\";\n\nexport type Format = \"json\" | \"pretty\" | \"raw\";\n\nexport function formatEvent(event: Event, format: Format, decrypted: unknown | undefined): string {\n switch (format) {\n case \"json\": {\n const value: Record<string, unknown> = { ...event };\n if (decrypted !== undefined) value.decrypted = decrypted;\n return JSON.stringify(value);\n }\n case \"pretty\": {\n const lines: string[] = [`=== ${event.eventType} ===`];\n if (event.createdAt) lines.push(` at: ${event.createdAt}`);\n if (event.streamId) lines.push(` stream: ${event.streamId}`);\n if (event.actor) {\n const a = event.actor;\n lines.push(` actor: ${a.name ? `${a.name} (${a.publicId})` : a.publicId}`);\n if (a.devicePublicId || a.deviceName) {\n lines.push(` device: ${a.deviceName ? `${a.deviceName} (${a.devicePublicId ?? \"?\"})` : a.devicePublicId}`);\n }\n }\n if (event.encryption) {\n const enc = event.encryption.type === \"personal\"\n ? `personal (${event.encryption.passwordFingerprint})`\n : `org (v${event.encryption.v})`;\n lines.push(` encryption: ${enc}`);\n }\n if (decrypted !== undefined) lines.push(` decrypted: ${prettyValue(decrypted)}`);\n else lines.push(` data: ${prettyValue(event.data)}`);\n return lines.join(\"\\n\") + \"\\n\";\n }\n case \"raw\": {\n const payload = decrypted ?? event.data;\n return extractRaw(payload) ?? JSON.stringify(payload);\n }\n }\n}\n\nfunction prettyValue(v: unknown): string {\n try { return JSON.stringify(v, null, 2); }\n catch { return String(v); }\n}\n\nfunction extractRaw(v: unknown): string | undefined {\n if (!v || typeof v !== \"object\") return undefined;\n const obj = v as Record<string, unknown>;\n for (const k of [\"text\", \"value\", \"selectedValue\", \"url\", \"presignedGetUrl\", \"objectKey\"]) {\n const x = obj[k];\n if (typeof x === \"string\") return x;\n }\n return undefined;\n}\n","// `sp events` — stream the raw event feed as a Stream pipeline:\n//\n// source ─ quiet-exit (timeoutTo) ─ --until (takeWhile) ─ --type (filter)\n// ─ decrypt+print (mapEffect) ─ --limit (take) ─ runDrain\n//\n// Interruption anywhere (Ctrl-C, timeout) unwinds the scope, which aborts the\n// SDK stream via its AbortSignal (see services/sdk.ts) and closes the client.\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Ref, Stream } from \"effect\";\nimport { TypeFilter, tryDecryptEventData, type Event } from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n mappedText,\n passwordOption,\n quietOption,\n requireApiToken,\n topicOption,\n} from \"../global-options.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { acquireClient, sdkCall, sdkStream } from \"../services/sdk.js\";\nimport { formatEvent, type Format } from \"../output.js\";\nimport { sharedWebSocketFactory } from \"../daemon/transport.js\";\nimport { resolveSince, resolveUntil } from \"../since.js\";\n\nconst eventTypeOption = Options.text(\"type\").pipe(\n Options.withDescription(\"Filter by event type. Repeatable.\"),\n Options.repeated,\n);\n\nconst sinceOption = mappedText(\"since\", resolveSince).pipe(\n Options.withDescription(\"Replay from this point. Accepts `24h`, `7d`, or an ISO 8601 timestamp.\"),\n Options.optional,\n);\n\nconst untilOption = mappedText(\"until\", resolveUntil).pipe(\n Options.withDescription(\"Stop at this timestamp. Forces a finite range, so `--follow` is ignored.\"),\n Options.optional,\n);\n\nconst limitOption = Options.integer(\"limit\").pipe(\n Options.withDescription(\"Maximum number of events to print, then exit.\"),\n Options.optional,\n);\n\nconst followOption = Options.boolean(\"follow\").pipe(\n Options.withAlias(\"f\"),\n Options.withDescription(\"After history is drained, keep streaming live instead of exiting. Only meaningful with `--since`.\"),\n);\n\nconst formatOption = Options.choice(\"format\", [\"json\", \"pretty\", \"raw\"] as const).pipe(\n Options.withDescription(\"Output format.\"),\n Options.withDefault(\"json\" as Format),\n);\n\nconst directOption = Options.boolean(\"direct\").pipe(\n Options.withDescription(\"Open an independent WS connection. By default a LIVE stream (no --since) shares ONE broker connection across all sp processes; --direct bypasses it. A --since history replay always goes direct.\"),\n);\n\nconst QUIET_EXIT_AFTER_DRAIN = \"2 seconds\";\n\nexport const eventsCommand = Command.make(\n \"events\",\n {\n type: eventTypeOption,\n since: sinceOption,\n until: untilOption,\n limit: limitOption,\n follow: followOption,\n format: formatOption,\n direct: directOption,\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const apiToken = yield* requireApiToken(args[\"api-token\"]);\n const baseUrl = args[\"base-url\"];\n\n const sinceIso = Option.getOrUndefined(args.since);\n const untilDate = Option.getOrUndefined(args.until);\n const limit = Option.getOrUndefined(args.limit);\n\n const filter = new TypeFilter(args.type);\n yield* Effect.forEach(filter.unknown, (u) => out.warn(`ignoring unknown --type \\`${u}\\``));\n\n // A live stream (no --since) shares the broker by default; a historical\n // replay (--since) goes direct — the broker only holds a recent window,\n // so it can't serve a deep backlog. `--direct` forces direct either way.\n const webSocketFactory =\n args.direct || sinceIso !== undefined\n ? Option.none()\n : yield* sharedWebSocketFactory({ credential: { kind: \"personal\", apiToken }, baseUrl });\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireClient({\n baseUrl,\n apiToken,\n passwords: args.password,\n ...Option.match(webSocketFactory, { onNone: () => ({}), onSome: (f) => ({ webSocketFactory: f }) }),\n });\n\n // includePasswordSalt also derives the account default key from a bare\n // `--password`, so submission bodies decrypt too.\n const keyring =\n args.password.length > 0\n ? yield* sdkCall(\"keyring\", () => client.keyring({ includePasswordSalt: true }))\n : undefined;\n if (keyring) {\n if (keyring.size > 0) yield* out.info(`keyring built with ${keyring.size} fingerprint(s)`);\n else yield* out.warn(`no symmetric keys derived from --password (count=${args.password.length})`);\n }\n\n yield* out.info(\n `connecting to ${baseUrl.replace(/\\/+$/, \"\")}/ws/v1/events${sinceIso ? `?since=${sinceIso}` : \"\"}`,\n );\n\n // A pure history replay exits shortly after the backlog drains.\n const exitWhenQuiet = sinceIso !== undefined && !args.follow && untilDate === undefined;\n const printed = yield* Ref.make(0);\n const endNote = yield* Ref.make(Option.none<string>());\n const note = (msg: string) => Ref.set(endNote, Option.some(msg));\n\n const untilReached = (ev: Event): boolean => {\n if (untilDate === undefined || !ev.createdAt) return false;\n const t = new Date(ev.createdAt);\n return Number.isFinite(t.getTime()) && t >= untilDate;\n };\n\n const source = sdkStream(\"events stream\", (signal) =>\n client.events({ ...(sinceIso !== undefined ? { since: sinceIso } : {}), signal }),\n );\n\n yield* source.pipe(\n // Quiet-exit: if no event arrives for 2s, swap to a stream that just\n // records the reason and ends — the scope closing aborts the WS.\n exitWhenQuiet\n ? Stream.timeoutTo(\n QUIET_EXIT_AFTER_DRAIN,\n Stream.drain(Stream.fromEffect(note(\"history drained, exiting (use --follow to keep streaming)\"))),\n )\n : (s) => s,\n untilDate !== undefined\n ? Stream.takeUntilEffect((ev: Event) =>\n untilReached(ev) ? note(\"--until reached, exiting\").pipe(Effect.as(true)) : Effect.succeed(false),\n )\n : (s) => s,\n // takeUntil emits the boundary element too; --until is exclusive.\n Stream.filter((ev) => !untilReached(ev) && filter.matches(ev)),\n Stream.mapEffect((ev) =>\n Effect.gen(function* () {\n const decrypted = keyring\n ? yield* sdkCall(\"decrypt event\", () => tryDecryptEventData(ev, keyring))\n : undefined;\n yield* out.print(formatEvent(ev, args.format, decrypted));\n const n = yield* Ref.updateAndGet(printed, (x) => x + 1);\n if (limit !== undefined && n >= limit) yield* note(`--limit ${limit} reached, exiting`);\n }),\n ),\n limit !== undefined ? Stream.take(limit) : (s) => s,\n Stream.runDrain,\n );\n\n yield* Ref.get(endNote).pipe(\n Effect.flatMap(Option.match({ onNone: () => Effect.void, onSome: (msg) => out.info(msg) })),\n );\n const total = yield* Ref.get(printed);\n if (args.format === \"raw\" && total === 0) yield* out.warn(\"no events matched\");\n }),\n );\n }),\n);\n","// Parse `--text-input \"desc;required=true\"` style values into wire `Input`s.\n\nimport type { Action, Input } from \"@simplepush/sdk\";\n\nexport type InputSpec = {\n description?: string;\n required: boolean;\n defaultValue?: string;\n // Choice-only multi-select settings. `multi` defaults to false (single\n // choice); `minSelections`/`maxSelections` are only meaningful when multi.\n multi?: boolean;\n minSelections?: number;\n maxSelections?: number;\n};\n\nexport type InputKind = \"text\" | \"choice\" | \"actions\" | \"photo\" | \"voiceRecording\" | \"file\" | \"location\";\n\nconst SETTING_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;\n\nfunction looksLikeSetting(seg: string): boolean {\n return SETTING_RE.test(seg);\n}\n\nfunction supportedKeys(kind: InputKind): string[] {\n if (kind === \"text\") return [\"required\", \"defaultValue\"];\n if (kind === \"choice\") return [\"required\", \"multi\", \"minSelections\", \"maxSelections\"];\n return [\"required\"];\n}\n\nfunction parseBool(s: string): boolean {\n if (s === \"true\" || s === \"yes\" || s === \"1\") return true;\n if (s === \"false\" || s === \"no\" || s === \"0\") return false;\n throw new Error(`expected boolean (true/false), got \\`${s}\\``);\n}\n\nfunction parseCount(s: string, floor: number): number {\n const n = Number(s);\n if (!Number.isInteger(n) || n < floor) throw new Error(`expected an integer >= ${floor}, got \\`${s}\\``);\n return n;\n}\n\nfunction applySetting(spec: InputSpec, seg: string, kind: InputKind): void {\n const eq = seg.indexOf(\"=\");\n if (eq < 0) throw new Error(`expected \\`key=value\\` segment, got \\`${seg}\\``);\n const key = seg.slice(0, eq).trim();\n const value = seg.slice(eq + 1).trim();\n if (key === \"required\") {\n spec.required = parseBool(value);\n return;\n }\n if (key === \"defaultValue\") {\n if (kind !== \"text\") throw new Error(\"`defaultValue=` is not supported on this input type (only --text-input)\");\n spec.defaultValue = value;\n return;\n }\n if (key === \"multi\") {\n if (kind !== \"choice\") throw new Error(\"`multi=` is not supported on this input type (only --choice-input)\");\n spec.multi = parseBool(value);\n return;\n }\n if (key === \"minSelections\") {\n if (kind !== \"choice\") throw new Error(\"`minSelections=` is not supported on this input type (only --choice-input)\");\n // 0 is allowed: it permits a deliberate empty answer (overrides the\n // required-implied floor of 1).\n spec.minSelections = parseCount(value, 0);\n return;\n }\n if (key === \"maxSelections\") {\n if (kind !== \"choice\") throw new Error(\"`maxSelections=` is not supported on this input type (only --choice-input)\");\n spec.maxSelections = parseCount(value, 1);\n return;\n }\n throw new Error(`unknown input setting \\`${key}=\\`; supported: ${supportedKeys(kind).join(\", \")}`);\n}\n\n// Splits `s` on unescaped `sep`. `\\<sep>` and `\\\\` unescape. Other backslashes pass through.\nfunction splitUnescaped(s: string, sep: string): string[] {\n const out: string[] = [];\n let cur = \"\";\n for (let i = 0; i < s.length; i++) {\n const c = s[i]!;\n if (c === \"\\\\\") {\n const next = s[i + 1];\n if (next === sep || next === \"\\\\\") { cur += next; i += 1; continue; }\n cur += c;\n continue;\n }\n if (c === sep) { out.push(cur); cur = \"\"; continue; }\n cur += c;\n }\n out.push(cur);\n return out;\n}\n\nexport function parseInputSpec(raw: string, kind: InputKind): InputSpec {\n const segments = splitUnescaped(raw, \";\");\n const first = segments.shift() ?? \"\";\n const spec: InputSpec = { required: true };\n if (first !== \"\") spec.description = first;\n for (const seg of segments) applySetting(spec, seg, kind);\n return spec;\n}\n\nexport function parseChoiceSpec(raw: string): { spec: InputSpec; options: string[] } {\n const segments = splitUnescaped(raw, \";\");\n const nonSettings: string[] = [];\n const settings: string[] = [];\n for (const seg of segments) (looksLikeSetting(seg) ? settings : nonSettings).push(seg);\n\n let description: string | undefined;\n let optionsRaw: string;\n if (nonSettings.length === 0) {\n throw new Error(\"--choice-input requires a comma-separated options list\");\n } else if (nonSettings.length === 1) {\n optionsRaw = nonSettings[0]!;\n } else if (nonSettings.length === 2) {\n description = nonSettings[0] !== \"\" ? nonSettings[0] : undefined;\n optionsRaw = nonSettings[1]!;\n } else {\n throw new Error(\"--choice-input has too many `;`-separated non-setting segments (expected `[description;]options[;key=value...]`)\");\n }\n\n const options = optionsRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (options.length === 0) throw new Error(\"--choice-input options list is empty\");\n\n const spec: InputSpec = { required: true };\n if (description !== undefined) spec.description = description;\n for (const seg of settings) applySetting(spec, seg, \"choice\");\n return { spec, options };\n}\n\nconst VALID_ACTION_STYLES = new Set([\"default\", \"primary\", \"destructive\"]);\n\n// `key=Label[:style]`. Split on the FIRST `=` (key vs the rest) and the LAST `:`\n// (label vs style) — but only peel off `:style` when the suffix is an actual\n// style, so a label legitimately containing a colon (\"Open: settings\") survives.\nfunction parseActionToken(token: string): Action {\n const eq = token.indexOf(\"=\");\n if (eq < 0) throw new Error(`each action must be \\`key=Label[:style]\\`, got \\`${token}\\``);\n const key = token.slice(0, eq).trim();\n let label = token.slice(eq + 1).trim();\n if (key.length === 0) throw new Error(`action key must not be empty in \\`${token}\\``);\n if (label.length === 0) throw new Error(`action label must not be empty in \\`${token}\\``);\n let style: Action[\"style\"] | undefined;\n const lastColon = label.lastIndexOf(\":\");\n if (lastColon >= 0) {\n const maybe = label.slice(lastColon + 1).trim();\n if (VALID_ACTION_STYLES.has(maybe)) {\n style = maybe as Action[\"style\"];\n label = label.slice(0, lastColon).trim();\n if (label.length === 0) throw new Error(`action label must not be empty in \\`${token}\\``);\n }\n }\n return style ? { key, label, style } : { key, label };\n}\n\n// `[description;]key=Label[:style],...[;required=...]`. Each `key=Label` action\n// token matches the generic SETTING_RE, so actions can't be detected that way —\n// only `required=` is treated as a setting; everything else is positional\n// (description / action-list) exactly like a choice spec.\nexport function parseActionsSpec(raw: string): { spec: InputSpec; actions: Action[] } {\n const segments = splitUnescaped(raw, \";\");\n const settings: string[] = [];\n const nonSettings: string[] = [];\n for (const seg of segments) (/^\\s*required\\s*=/.test(seg) ? settings : nonSettings).push(seg);\n\n let description: string | undefined;\n let actionsRaw: string;\n if (nonSettings.length === 0) {\n throw new Error(\"--action-input requires a comma-separated `key=Label[:style]` list\");\n } else if (nonSettings.length === 1) {\n actionsRaw = nonSettings[0]!;\n } else if (nonSettings.length === 2) {\n description = nonSettings[0] !== \"\" ? nonSettings[0] : undefined;\n actionsRaw = nonSettings[1]!;\n } else {\n throw new Error(\"--action-input has too many `;`-separated non-setting segments (expected `[description;]key=Label[:style],...[;required=...]`)\");\n }\n\n const actions = splitUnescaped(actionsRaw, \",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n .map(parseActionToken);\n if (actions.length === 0) throw new Error(\"--action-input list is empty\");\n // Keys are E2E-encrypted before send, so the backend can't enforce uniqueness;\n // catch it here for a clear error (parseActionToken already rejects empties).\n const seenKeys = new Set<string>();\n for (const a of actions) {\n if (seenKeys.has(a.key)) throw new Error(`duplicate action key: \\`${a.key}\\``);\n seenKeys.add(a.key);\n }\n\n const spec: InputSpec = { required: true };\n if (description !== undefined) spec.description = description;\n for (const seg of settings) applySetting(spec, seg, \"actions\");\n return { spec, actions };\n}\n\nexport type SliderConfig = { min: number; max: number; step?: number; unit?: string; defaultValue?: number };\n\nfunction parseNum(s: string, name: string): number {\n const n = Number(s);\n if (!Number.isFinite(n)) throw new Error(`slider \\`${name}\\` must be a number, got \\`${s}\\``);\n return n;\n}\n\n// `[description;]min=..;max=..[;step=..][;unit=..][;default=..][;required=..]`.\n// The description is the first segment only when it isn't a `key=value` setting;\n// `min`/`max` are required.\nexport function parseSliderSpec(raw: string): { spec: InputSpec; slider: SliderConfig } {\n const segments = splitUnescaped(raw, \";\");\n let description: string | undefined;\n const settings: string[] = [];\n segments.forEach((seg, i) => {\n if (i === 0 && !looksLikeSetting(seg)) {\n if (seg !== \"\") description = seg;\n } else {\n settings.push(seg);\n }\n });\n\n let min: number | undefined, max: number | undefined, step: number | undefined, defaultValue: number | undefined;\n let unit: string | undefined;\n let required = true;\n for (const seg of settings) {\n const eq = seg.indexOf(\"=\");\n if (eq < 0) throw new Error(`expected \\`key=value\\` segment, got \\`${seg}\\``);\n const key = seg.slice(0, eq).trim();\n const value = seg.slice(eq + 1).trim();\n switch (key) {\n case \"min\": min = parseNum(value, \"min\"); break;\n case \"max\": max = parseNum(value, \"max\"); break;\n case \"step\": step = parseNum(value, \"step\"); break;\n case \"unit\": unit = value; break;\n case \"default\": defaultValue = parseNum(value, \"default\"); break;\n case \"required\": required = parseBool(value); break;\n default: throw new Error(`unknown slider setting \\`${key}=\\`; supported: min, max, step, unit, default, required`);\n }\n }\n if (min === undefined || max === undefined) throw new Error(\"--slider-input requires `min=` and `max=`\");\n if (min >= max) throw new Error(\"--slider-input `min` must be less than `max`\");\n if (step !== undefined && step <= 0) throw new Error(\"--slider-input `step` must be positive\");\n if (defaultValue !== undefined && (defaultValue < min || defaultValue > max)) {\n throw new Error(\"--slider-input `default` must be within [min, max]\");\n }\n\n const spec: InputSpec = { required };\n if (description !== undefined) spec.description = description;\n const slider: SliderConfig = {\n min,\n max,\n ...(step !== undefined ? { step } : {}),\n ...(unit !== undefined && unit !== \"\" ? { unit } : {}),\n ...(defaultValue !== undefined ? { defaultValue } : {}),\n };\n return { spec, slider };\n}\n\n// --- spec -> wire `Input` builders ----------------------------------------\n// Shared by both `sp task` and `sp subtask` so the two commands send identical\n// input JSON and can't drift apart.\n// These build the PLAINTEXT input objects; the SDK encrypts each field\n// (description / options / labels / slider config) on send, identically for\n// tasks and subtasks.\n\nfunction textInputJson(spec: InputSpec): Input {\n const out: Input = { type: \"text\", required: spec.required };\n if (spec.description !== undefined) out.description = spec.description;\n if (spec.defaultValue !== undefined) out.defaultValue = spec.defaultValue;\n return out;\n}\n\nfunction simpleInputJson(\n type: \"photo\" | \"voiceRecording\" | \"file\" | \"location\",\n spec: InputSpec,\n): Input {\n const out: Input = { type, required: spec.required };\n if (spec.description !== undefined) out.description = spec.description;\n return out;\n}\n\nfunction choiceInputJson(spec: InputSpec, options: string[]): Input {\n const out: Input = { type: \"choice\", required: spec.required, options };\n if (spec.description !== undefined) out.description = spec.description;\n // Multi-select (omit `multi` when false, like the wire contract); the\n // min/max caps are only meaningful when multi.\n if (spec.multi) out.multi = true;\n if (spec.minSelections !== undefined) out.minSelections = spec.minSelections;\n if (spec.maxSelections !== undefined) out.maxSelections = spec.maxSelections;\n return out;\n}\n\nfunction sliderInputJson(spec: InputSpec, slider: SliderConfig): Input {\n const out: Input = {\n type: \"slider\",\n required: spec.required,\n min: slider.min,\n max: slider.max,\n ...(slider.step !== undefined ? { step: slider.step } : {}),\n ...(slider.unit !== undefined ? { unit: slider.unit } : {}),\n ...(slider.defaultValue !== undefined ? { defaultValue: slider.defaultValue } : {}),\n };\n if (spec.description !== undefined) out.description = spec.description;\n return out;\n}\n\nfunction actionsInputJson(spec: InputSpec, actions: Action[]): Input {\n const out: Input = { type: \"actions\", required: spec.required, actions };\n if (spec.description !== undefined) out.description = spec.description;\n return out;\n}\n\n/** Raw values of the repeatable `--*-input` flags. Each field is optional so a\n * caller that doesn't expose a given flag (or just got none) can omit it. */\nexport type InputArgs = {\n \"text-input\"?: ReadonlyArray<string>;\n \"choice-input\"?: ReadonlyArray<string>;\n \"action-input\"?: ReadonlyArray<string>;\n \"slider-input\"?: ReadonlyArray<string>;\n \"photo-input\"?: ReadonlyArray<string>;\n \"voice-recording-input\"?: ReadonlyArray<string>;\n \"file-input\"?: ReadonlyArray<string>;\n \"location-input\"?: ReadonlyArray<string>;\n};\n\n/** Parse every `--*-input` flag into the ordered `Input[]` sent on the wire.\n * Shared between `sp task` and `sp subtask`. Inputs are emitted grouped by kind\n * (text, choice, actions, slider, photo, voice, file, location). */\nexport function buildInputs(args: InputArgs): Input[] {\n const out: Input[] = [];\n for (const raw of args[\"text-input\"] ?? []) out.push(textInputJson(parseInputSpec(raw, \"text\")));\n for (const raw of args[\"choice-input\"] ?? []) {\n const { spec, options } = parseChoiceSpec(raw);\n out.push(choiceInputJson(spec, options));\n }\n for (const raw of args[\"action-input\"] ?? []) {\n const { spec, actions } = parseActionsSpec(raw);\n out.push(actionsInputJson(spec, actions));\n }\n for (const raw of args[\"slider-input\"] ?? []) {\n const { spec, slider } = parseSliderSpec(raw);\n out.push(sliderInputJson(spec, slider));\n }\n for (const raw of args[\"photo-input\"] ?? []) out.push(simpleInputJson(\"photo\", parseInputSpec(raw, \"photo\")));\n for (const raw of args[\"voice-recording-input\"] ?? []) out.push(simpleInputJson(\"voiceRecording\", parseInputSpec(raw, \"voiceRecording\")));\n for (const raw of args[\"file-input\"] ?? []) out.push(simpleInputJson(\"file\", parseInputSpec(raw, \"file\")));\n for (const raw of args[\"location-input\"] ?? []) out.push(simpleInputJson(\"location\", parseInputSpec(raw, \"location\")));\n return out;\n}\n","// `sp notify` — admin-side notification send for org-bound CLIs. Targets\n// either a named member, a broadcast, or an org topic. Auto-encrypts under\n// the org master_key when the local vault is unlocked, falling back to\n// plaintext otherwise (or when --no-encrypt is passed).\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Schema } from \"effect\";\nimport {\n buildOrgNotificationRequest,\n isNotificationGroupResponse,\n type OrgSendTarget,\n type SendNotificationOptions,\n type NotificationMedia,\n type NotificationInput,\n type CreateNotificationResponse,\n} from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n requireApiToken,\n topicOption,\n willEncrypt,\n} from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { formatSent, type Member } from \"../collect-output.js\";\nimport { Api } from \"../services/api.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { acquireClient, sdkCall } from \"../services/sdk.js\";\nimport { parseActionsSpec } from \"../input-spec.js\";\n\n// Supported notification media content types (mirror the backend allow-list /\n// iOS UTIs) + extension → MIME. image/* renders on iOS + Android; audio/* is iOS-only.\nconst NOTIFY_MEDIA_TYPES: Record<\"image\" | \"audio\", Set<string>> = {\n image: new Set([\"image/jpeg\", \"image/png\", \"image/gif\"]),\n audio: new Set([\"audio/aiff\", \"audio/x-aiff\", \"audio/wav\", \"audio/x-wav\", \"audio/vnd.wave\", \"audio/mpeg\", \"audio/mp3\", \"audio/mp4\", \"audio/aac\", \"audio/x-m4a\"]),\n};\nconst NOTIFY_MEDIA_EXT: Record<string, string> = {\n png: \"image/png\", jpg: \"image/jpeg\", jpeg: \"image/jpeg\", gif: \"image/gif\",\n aiff: \"audio/aiff\", aif: \"audio/aiff\", wav: \"audio/wav\", mp3: \"audio/mpeg\", m4a: \"audio/mp4\", aac: \"audio/aac\",\n};\n\n/** Derive + validate the media content type from a URL's extension, or null if\n * unsupported for the kind. */\nfunction notifyMediaContentType(url: string, kind: \"image\" | \"audio\"): string | null {\n const clean = url.split(\"?\")[0] ?? url;\n const ext = clean.slice(clean.lastIndexOf(\".\") + 1).toLowerCase();\n const ct = NOTIFY_MEDIA_EXT[ext];\n return ct && NOTIFY_MEDIA_TYPES[kind].has(ct) ? ct : null;\n}\n\nconst contentOption = Options.text(\"content\").pipe(\n Options.withDescription(\"Notification body. Encrypted under the org master_key when the vault is unlocked, plaintext otherwise.\"),\n);\n\n// No `-t` short alias: `-t` is the shared `--api-token` flag (see global-options),\n// used by the personal send path. Use `--title` for the notification title.\nconst titleOption = Options.text(\"title\").pipe(\n Options.withDescription(\"Optional notification title shown above the body on the recipient's lock screen.\"),\n Options.optional,\n);\n\nconst memberOption = Options.text(\"member\").pipe(\n Options.withAlias(\"m\"),\n Options.withDescription(\"Send to a single org member by display name (case-insensitive). Org send; mutually exclusive with --broadcast, --org-topic, and -k/--topic.\"),\n Options.optional,\n);\n\nconst broadcastOption = Options.boolean(\"broadcast\").pipe(\n Options.withAlias(\"b\"),\n Options.withDescription(\"Send to every member of the org. Org send; mutually exclusive with --member, --org-topic, and -k/--topic.\"),\n);\n\nconst orgTopicOption = Options.text(\"org-topic\").pipe(\n Options.withAlias(\"o\"),\n Options.withDescription(\"Send to an org topic by value (from `sp org topics list`). Org send; mutually exclusive with --member, --broadcast, and -k/--topic (the personal topic).\"),\n Options.optional,\n);\n\nconst tagOption = Options.text(\"tag\").pipe(\n Options.withDescription(\"Optional notification tag — recipients can use it to coalesce / replace prior notifications with the same tag.\"),\n Options.optional,\n);\n\nconst imageOption = Options.text(\"image\").pipe(\n Options.withDescription(\"Image URL to show in the push (PNG/JPEG/GIF). Renders on iOS + Android. Mutually exclusive with --audio. URLs only — file uploads are SDK-only.\"),\n Options.optional,\n);\n\nconst audioOption = Options.text(\"audio\").pipe(\n Options.withDescription(\"Audio URL to play inline in the push (AIFF/WAV/MP3/M4A; iOS only). Mutually exclusive with --image. URLs only.\"),\n Options.optional,\n);\n\n// Explicit opt-out: even with an unlocked vault, don't encrypt. Useful for\n// sanity-checking the plaintext path, or for org broadcasts that should remain\n// readable by API integrations on the receiving end.\nconst noEncryptOption = Options.boolean(\"no-encrypt\").pipe(\n Options.withDescription(\"Send the body in plaintext even when an unlocked vault is available.\"),\n);\n\n// A notification Action input: tap-buttons (e.g. Accept/Deny) the recipient\n// answers with. A notification carries at most one input, so this is a single\n// option (not repeatable like `sp task`'s --action-input); it reuses the task\n// action-input parsing grammar, minus the `primary` style (notifications only\n// support default|destructive). On an encrypted send the SDK seals each action's\n// `key` AND `label` (like a choice option, and like `sp task`'s actions input);\n// only `style` stays plaintext.\nconst actionInputOption = Options.text(\"action-input\").pipe(\n Options.withAlias(\"a\"),\n Options.withDescription(\"Add an actions input: tap-buttons the recipient answers with (e.g. Accept/Deny). Format: `[description;]key=Label[:style],...`, actions comma-separated; style is default|destructive. Use `\\\\,` for a literal comma in a label. A notification carries at most one input.\"),\n Options.optional,\n);\n\n// A free-text reply input: the recipient types an answer. Boolean flag (no\n// value); a notification carries at most one input, so it's mutually exclusive\n// with --choice-input / --action-input.\nconst textInputOption = Options.boolean(\"text-input\").pipe(\n Options.withDescription(\"Add a free-text reply input the recipient types an answer into. Mutually exclusive with --choice-input / --action-input (a notification carries at most one input).\"),\n);\n\n// A single-choice input: a comma-separated options list the recipient picks ONE\n// of. Notifications support single-select only (no multi, unlike `sp task`).\nconst choiceInputOption = Options.text(\"choice-input\").pipe(\n Options.withAlias(\"c\"),\n Options.withDescription(\"Add a single-choice input: a comma-separated options list (e.g. \\\"Approve,Deny\\\") the recipient picks one of. Notifications are single-select only. Mutually exclusive with --text-input / --action-input.\"),\n Options.optional,\n);\n\n// Recipient-state model. Default (flag absent) = independent: each recipient\n// gets their OWN notification instance, tied together by a `grpntf_` group.\n// `--shared` = the single shared notification the first reply completes\n// for everyone. Mirrors `sp task`'s --shared.\nconst sharedOption = Options.boolean(\"shared\").pipe(\n Options.withDescription(\"Shared mode: ONE notification all recipients see and answer together (the first reply completes it for everyone). Default (without this flag) is independent mode: every recipient gets their own notification instance under a group.\"),\n);\n\n// stdout shape (mirrors `sp task --format`): `text` prints the bare id\n// (default, human/script), `json` prints a machine-readable `sent` line that\n// `sp collect` consumes to know the members + resume point.\nconst formatOption = Options.choice(\"format\", [\"text\", \"json\"] as const).pipe(\n Options.withDescription(\"stdout format for a send: `text` (the bare id, default) or `json` (a `sent` line piped to `sp collect`).\"),\n Options.withDefault(\"text\"),\n);\n\n/** Parse the (at most one) notification input off the three flags. */\nconst parseNotificationInput = (\n textInputOn: boolean,\n choiceRaw: string | undefined,\n actionRaw: string | undefined,\n): Effect.Effect<NotificationInput | undefined, UserError> =>\n Effect.gen(function* () {\n if ([textInputOn, choiceRaw !== undefined, actionRaw !== undefined].filter(Boolean).length > 1) {\n return yield* Effect.fail(\n new UserError({ message: \"a notification carries at most one input: pass only one of --text-input, --choice-input, or --action-input\" }),\n );\n }\n if (textInputOn) return { type: \"text\" } as const;\n if (choiceRaw !== undefined) {\n const options = choiceRaw.split(\",\").map((o) => o.trim()).filter((o) => o.length > 0);\n if (options.length === 0) {\n return yield* Effect.fail(new UserError({ message: \"--choice-input needs at least one comma-separated option\" }));\n }\n return { type: \"choice\", options } as const;\n }\n if (actionRaw !== undefined) {\n const actions = yield* Effect.try({\n try: () => parseActionsSpec(actionRaw).actions,\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n const primary = actions.find((a) => a.style === \"primary\");\n if (primary !== undefined) {\n return yield* Effect.fail(\n new UserError({ message: `notification action styles must be 'default' or 'destructive' (got 'primary' on key \\`${primary.key}\\`) — 'primary' is task-only` }),\n );\n }\n return {\n type: \"actions\",\n actions: actions.map((a) => ({\n key: a.key,\n label: a.label,\n ...(a.style !== undefined ? { style: a.style as \"default\" | \"destructive\" } : {}),\n })),\n } as const;\n }\n return undefined;\n });\n\nexport const notifyCommand = Command.make(\n \"notify\",\n {\n content: contentOption,\n title: titleOption,\n member: memberOption,\n broadcast: broadcastOption,\n \"org-topic\": orgTopicOption,\n tag: tagOption,\n image: imageOption,\n audio: audioOption,\n \"text-input\": textInputOption,\n \"choice-input\": choiceInputOption,\n \"action-input\": actionInputOption,\n shared: sharedOption,\n format: formatOption,\n noEncrypt: noEncryptOption,\n // Personal send: `-k/--topic` sends to a personal topic; omitting every\n // target is a note-to-self. These carry the personal credential + keys.\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const memberName = Option.getOrUndefined(args.member);\n const orgTopicName = Option.getOrUndefined(args[\"org-topic\"]);\n const personalTopic = args.topic[0];\n const isOrgTarget = memberName !== undefined || args.broadcast || orgTopicName !== undefined;\n // At most one of: org member / broadcast / org-topic, or a personal topic.\n // ZERO targets = a personal note-to-self (your own devices).\n const targetCount = [memberName !== undefined, args.broadcast, orgTopicName !== undefined, personalTopic !== undefined].filter(Boolean).length;\n if (targetCount > 1) {\n return yield* Effect.fail(\n new UserError({ message: \"pass at most one target: -m <member> | -b (broadcast) | -o <org-topic> | -t <topic> (omit all for a self-send)\" }),\n );\n }\n\n const titleOpt = Option.getOrUndefined(args.title);\n const tagOpt = Option.getOrUndefined(args.tag);\n const message = args.content;\n\n // Single optional media (image XOR audio) as a link, validated once for\n // both send paths. The org path wraps it in a `NotificationMedia`; the\n // personal path passes the URL straight to the SDK (which re-validates).\n const imageUrl = Option.getOrUndefined(args.image);\n const audioUrl = Option.getOrUndefined(args.audio);\n if (imageUrl !== undefined && audioUrl !== undefined) {\n return yield* Effect.fail(new UserError({ message: \"only one of --image or --audio may be set\" }));\n }\n const mediaUrl = imageUrl ?? audioUrl;\n const mediaKind = imageUrl !== undefined ? \"image\" : \"audio\";\n let mediaContentType: string | undefined;\n if (mediaUrl !== undefined) {\n if (!/^https?:\\/\\//.test(mediaUrl)) {\n return yield* Effect.fail(\n new UserError({ message: \"notification media from the CLI must be an http(s) URL; file uploads aren't supported here (use the SDK)\" }),\n );\n }\n const contentType = notifyMediaContentType(mediaUrl, mediaKind);\n if (contentType === null) {\n return yield* Effect.fail(\n new UserError({ message: `--${mediaKind} URL must point to a supported ${mediaKind} type (by extension); got \"${mediaUrl}\"` }),\n );\n }\n mediaContentType = contentType;\n }\n\n // At most one notification input: text (free-text reply), choice\n // (single-select options), or actions (tap-buttons). The SDK encrypts the\n // choice options / action keys + labels on an encrypted send; a text input\n // has no payload to encrypt.\n const input = yield* parseNotificationInput(\n args[\"text-input\"],\n Option.getOrUndefined(args[\"choice-input\"]),\n Option.getOrUndefined(args[\"action-input\"]),\n );\n\n if (isOrgTarget) {\n // ---- Organization send: CLI bearer session + org-vault encryption ----\n const api = yield* Api;\n const access = yield* VaultAccess;\n\n const vault = yield* access.forSendOrPlaintext(args.noEncrypt);\n\n const target: OrgSendTarget =\n memberName !== undefined ? { member: memberName }\n : args.broadcast ? { broadcast: true }\n : { topic: orgTopicName! };\n const media: NotificationMedia | undefined =\n mediaUrl !== undefined ? { type: \"link\", url: mediaUrl, contentType: mediaContentType! } : undefined;\n\n // Reuse the SDK's notification field encryption + body shape (the same\n // OrgClient.sendNotification produces). Crucially this encrypts the tag,\n // which the receiver decrypts alongside title/content — a plaintext tag\n // on an encrypted notification fails the whole decrypt. We own the POST.\n const masterKey = vault\n ? { key: vault.masterKeyCurrent.key, version: vault.masterKeyCurrent.version }\n : undefined;\n const opts: SendNotificationOptions = {\n content: message,\n ...(titleOpt !== undefined ? { title: titleOpt } : {}),\n ...(tagOpt !== undefined ? { tag: tagOpt } : {}),\n ...(input !== undefined ? { input } : {}),\n ...(args.shared ? { shared: true } : {}),\n };\n const body = yield* sdkCall(\"build notification request\", () =>\n buildOrgNotificationRequest(target, opts, media, masterKey),\n );\n\n // CreateNotificationJsonRequest's codec flattens (decodes the bare\n // CreateNotificationData), so the body goes on the wire UNWRAPPED.\n const payload = (yield* api.postJson(\"notify\", \"/v1/org/notifications/json\", Schema.Unknown, body)) as CreateNotificationResponse;\n const encNote = vault ? ` (encrypted under master_key v${vault.masterKeyCurrent.version})` : \" (plaintext)\";\n if (isNotificationGroupResponse(payload)) {\n const n = payload.instances.length;\n yield* out.info(`notification group created: ${payload.groupId} (${n} recipient${n === 1 ? \"\" : \"s\"})${encNote}`);\n yield* Effect.forEach(payload.instances, (inst) => {\n const who = `${inst.recipient.publicId}${inst.recipient.name ? ` (${inst.recipient.name})` : \"\"}`;\n return out.info(`instance: ${who} -> ${inst.notificationId}`);\n });\n if (n === 0) yield* out.warn(\"the target has no recipients — the group is empty\");\n if (args.format === \"json\") {\n const members: Member[] = payload.instances.map((inst) => ({\n id: inst.notificationId,\n kind: \"notification\",\n recipient: { publicId: inst.recipient.publicId, name: inst.recipient.name ?? null },\n }));\n yield* out.print(formatSent(payload.groupId, payload.createdAt, members));\n } else {\n yield* out.print(payload.groupId);\n }\n } else {\n yield* out.info(`Notification sent${encNote}.`);\n yield* out.info(`Id: ${payload.notificationId}`);\n yield* out.info(`Created: ${payload.createdAt}`);\n if (args.format === \"json\") {\n yield* out.print(formatSent(undefined, payload.createdAt, [{ id: payload.notificationId, kind: \"notification\", recipient: null }]));\n } else {\n yield* out.print(payload.notificationId);\n }\n }\n return;\n }\n\n // ---- Personal send: a personal topic (-k) or a note-to-self (no target) ----\n // Uses the personal SDK Client (API-Token). A topic send encrypts under the\n // matching `password@topic`; a note-to-self encrypts under the account\n // default password (a bare --password), else plaintext.\n const apiToken = yield* requireApiToken(args[\"api-token\"]);\n const encrypting = willEncrypt(args.password, personalTopic);\n if (encrypting) yield* out.info(\"encrypting outgoing notification (Argon2id, this takes a moment)\");\n const encNote = encrypting ? \" (encrypted)\" : \" (plaintext)\";\n\n // Not annotated as SendNotificationOptions on purpose: that would widen\n // `shared` to boolean and pull `{...baseOpts, topic}` into the union\n // overload. Left inferred (no `shared` key) so the topic send resolves to\n // the NotificationGroup overload. Mirrors `sp task`'s baseOpts.\n const baseOpts = {\n content: message,\n ...(titleOpt !== undefined ? { title: titleOpt } : {}),\n ...(tagOpt !== undefined ? { tag: tagOpt } : {}),\n ...(input !== undefined ? { input } : {}),\n ...(imageUrl !== undefined ? { image: imageUrl } : {}),\n ...(audioUrl !== undefined ? { audio: audioUrl } : {}),\n };\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireClient({ baseUrl: args[\"base-url\"], apiToken, passwords: [...args.password] });\n\n // A bare-id (`text`) or `sent`-line (`json`) print for a single note.\n const printSingle = (note: { notificationId: string; createdAt: string }) =>\n args.format === \"json\"\n ? out.print(formatSent(undefined, note.createdAt, [{ id: note.notificationId, kind: \"notification\", recipient: null }]))\n : out.print(note.notificationId);\n\n if (personalTopic === undefined) {\n // Note to self: no topic → your own devices, a single Notification.\n const note = yield* sdkCall(\"notify\", () => client.sendNotification(baseOpts));\n yield* out.info(`self-send notification sent${encNote}.`);\n yield* out.info(`Id: ${note.notificationId}`);\n yield* printSingle(note);\n } else if (args.shared) {\n // Shared mode: one notification all topic subscribers share.\n const note = yield* sdkCall(\"notify\", () => client.sendNotification({ ...baseOpts, topic: personalTopic, shared: true }));\n yield* out.info(`notification sent${encNote}.`);\n yield* out.info(`Id: ${note.notificationId}`);\n yield* printSingle(note);\n } else {\n // Independent (default): one instance per subscriber under a group.\n const group = yield* sdkCall(\"notify\", () => client.sendNotification({ ...baseOpts, topic: personalTopic }));\n const n = group.instances.length;\n yield* out.info(`notification group created: ${group.groupId} (${n} recipient${n === 1 ? \"\" : \"s\"})${encNote}`);\n yield* Effect.forEach(group.instances, (inst) => {\n const who = `${inst.recipient?.publicId ?? \"unknown\"}${inst.recipient?.name ? ` (${inst.recipient.name})` : \"\"}`;\n return out.info(`instance: ${who} -> ${inst.notificationId}`);\n });\n if (n === 0) yield* out.warn(\"the topic has no recipients — the group is empty\");\n if (args.format === \"json\") {\n const members: Member[] = group.instances.map((inst) => ({\n id: inst.notificationId,\n kind: \"notification\",\n recipient: inst.recipient ? { publicId: inst.recipient.publicId, name: inst.recipient.name ?? null } : null,\n }));\n yield* out.print(formatSent(group.groupId, group.createdAt, members));\n } else {\n yield* out.print(group.groupId);\n }\n }\n }),\n );\n }),\n);\n","// `sp org encryption` — end-to-end encryption administration. The crypto\n// root is the org passphrase: it derives `vault_key` (Argon2id) which encrypts\n// the vault blob the server stores. Admins move between machines by\n// re-entering the passphrase; subsequent encryption-using commands on the\n// same machine prompt only if the local plaintext cache is absent. Members\n// never see the passphrase — they only get a per-device wrap of the org\n// master key after an admin runs `sync`.\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Schema } from \"effect\";\n\nimport { quietOption } from \"../global-options.js\";\nimport { Aborted } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { InviteStore, VaultStore } from \"../services/stores.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { Sodium } from \"../crypto/sodium.js\";\nimport { DEFAULT_KDF_PARAMS, type MasterKey, type VaultContents } from \"../crypto/index.js\";\n\n// ---------- HTTP shapes (mirror backend/model/v1/EncryptionModels.scala) ----------\n\n// Wire shape for per-device wrapped keys. Mirrors `domain.WrappedKey` on the\n// backend (version + base64 blob), used both in GET /org/encryption/devices\n// payloads and in PUT .../wraps bodies.\nconst WrappedKey = Schema.Struct({ version: Schema.Number, blob: Schema.String });\ntype WrappedKey = typeof WrappedKey.Type;\n\nconst OrgEncryptionDeviceSummary = Schema.Struct({\n deviceId: Schema.String,\n devicePubkeyB64: Schema.String,\n inviteHmacB64: Schema.String,\n wrappedKeys: Schema.Array(WrappedKey),\n});\ntype OrgEncryptionDeviceSummary = typeof OrgEncryptionDeviceSummary.Type;\n\nconst ListOrgEncryptionDevicesResponse = Schema.Struct({\n devices: Schema.Array(OrgEncryptionDeviceSummary),\n});\n\nconst fetchEncryptionDevices = Effect.gen(function* () {\n const api = yield* Api;\n const { devices } = yield* api.getJson(\n \"fetch encryption devices\",\n \"/v1/org/encryption/devices\",\n ListOrgEncryptionDevicesResponse,\n );\n return devices;\n});\n\n// ---------- enable ----------\n\nconst yesIWroteItDownOption = Options.boolean(\"i-saved-the-passphrase\").pipe(\n Options.withDescription(\n \"Confirm you have copied the passphrase somewhere safe. The passphrase is the only way to unlock the org's encryption vault on another machine; if lost, the only recovery is to re-enable encryption and re-onboard every device.\",\n ),\n);\n\nconst enableCommand = Command.make(\n \"enable\",\n { confirm: yesIWroteItDownOption, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const access = yield* VaultAccess;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n\n const existing = yield* access.fetchConfig;\n if (existing.enabled) {\n yield* out.error(\"encryption is already enabled for this org. Use `sp org encryption key rotate` to rotate the key.\");\n return yield* Effect.fail(new Aborted());\n }\n\n // Generate every secret locally; nothing in the vault touches the\n // backend in plaintext.\n const passphrase = yield* sodium.generatePassphrase();\n const vaultSalt = yield* sodium.generateVaultSalt;\n const adminKp = yield* sodium.generateAdminKeyPair;\n const masterKey = yield* sodium.generateMasterKey;\n\n const vaultContents: VaultContents = {\n adminPublicKey: adminKp.publicKey,\n adminPrivateKey: adminKp.privateKey,\n masterKeyCurrent: { version: 1, key: masterKey },\n masterKeyHistory: [],\n };\n\n const vaultKey = yield* sodium.deriveVaultKey(passphrase, vaultSalt, DEFAULT_KDF_PARAMS);\n const vaultBlob = yield* sodium.encryptVault(vaultContents, vaultKey);\n\n // Show the passphrase once and require explicit confirmation. We\n // intentionally do this BEFORE hitting the backend so an accidentally-\n // run `enable` doesn't leave the org in an enabled-but-passphrase-lost\n // state.\n yield* out.info(\"\");\n yield* out.info(\"=== ORG ENCRYPTION PASSPHRASE — copy this now, it will not be shown again ===\");\n yield* out.info(\"\");\n yield* out.print(` ${passphrase}`);\n yield* out.info(\"\");\n yield* out.info(\"This passphrase is the ONLY way to:\");\n yield* out.info(\" - unlock the encryption vault from another admin's machine\");\n yield* out.info(\" - recover access if your CLI state is lost\");\n yield* out.info(\"It cannot be recovered if forgotten. Re-enabling encryption forces\");\n yield* out.info(\"every member device to re-onboard from scratch.\");\n yield* out.info(\"\");\n if (!args.confirm) {\n yield* out.info(\"Re-run with --i-saved-the-passphrase to push this config to the server.\");\n return yield* Effect.fail(new Aborted());\n }\n\n yield* api.post(\"enable\", \"/v1/org/encryption/enable\", {\n adminPubkeyB64: sodium.toB64(adminKp.publicKey),\n vaultBlobB64: sodium.toB64(vaultBlob),\n vaultSaltB64: sodium.toB64(vaultSalt),\n kdfParams: DEFAULT_KDF_PARAMS,\n });\n\n yield* vaultStore.save(vaultContents);\n yield* out.info(\"Encryption enabled. master_key version 1 generated.\");\n yield* out.info(\"Local vault cached at ~/.config/simplepush/vault.json (subsequent commands won't prompt).\");\n }),\n);\n\n// ---------- status ----------\n\nconst statusCommand = Command.make(\"status\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const access = yield* VaultAccess;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n\n const cfg = yield* access.fetchConfig;\n if (!cfg.enabled) {\n yield* out.print(\"Encryption: disabled\");\n return yield* out.info(\"Run `org encryption enable` to set it up.\");\n }\n\n yield* out.print(\"Encryption: enabled\");\n yield* out.print(`Admin pubkey: ${cfg.adminPubkeyB64 ?? \"?\"}`);\n\n // Read-only here: status shouldn't prompt for the passphrase just to\n // refresh the display. `sync` (and other commands that actually need the\n // vault contents) prompts on demand.\n const vault = Option.getOrUndefined(yield* vaultStore.load);\n // A cache whose admin pubkey doesn't match the server config is stale (a\n // different login's vault) — the next vault-using command clears it.\n const cacheStale =\n vault !== undefined &&\n typeof cfg.adminPubkeyB64 === \"string\" &&\n !sodium.constantTimeEqual(vault.adminPublicKey, sodium.fromB64(cfg.adminPubkeyB64));\n yield* out.print(\n `Vault cache: ${\n vault\n ? cacheStale\n ? \"STALE (doesn't match this org — next encryption command re-prompts)\"\n : `present (master_key v${vault.masterKeyCurrent.version})`\n : \"absent (next encryption command will prompt)\"\n }`,\n );\n\n const devices = yield* fetchEncryptionDevices;\n // A stale cache's key version says nothing about this org's devices.\n const currentVersion = cacheStale ? undefined : vault?.masterKeyCurrent.version;\n const upToDate = currentVersion === undefined\n ? 0\n : devices.filter((d) => d.wrappedKeys.some((w) => w.version === currentVersion)).length;\n const pending = devices.length - upToDate;\n yield* out.print(`Devices onboarded: ${devices.length}`);\n if (currentVersion !== undefined) {\n yield* out.print(` current key wrapped: ${upToDate}`);\n yield* out.print(` pending sync: ${pending}`);\n if (pending > 0) yield* out.info(\"Run `org encryption sync` to wrap the current master key to pending devices.\");\n } else {\n yield* out.info(\"Local vault cache is absent — `org encryption sync` will prompt for the passphrase.\");\n }\n }),\n);\n\n// ---------- shared wrap loop ----------\n\ninterface SyncCounts {\n readonly wrapped: number;\n readonly alreadyCurrent: number;\n readonly unverified: number;\n readonly failed: number;\n}\n\n// Walks every onboarded device in the org and wraps `master_key_current` to\n// any that don't already hold it. Verifies the device pubkey against the\n// CLI's locally-stored invite codes (HMAC match) before wrapping so a\n// malicious backend can't substitute a pubkey it controls. Returns counts\n// so callers (sync, key rotate) can print a summary in their own voice.\nconst wrapCurrentKeyToAllDevices = (vault: VaultContents) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n const invites = yield* InviteStore;\n const sodium = yield* Sodium;\n\n const devices = yield* fetchEncryptionDevices;\n const knownInvites = yield* invites.listValid;\n const currentMaster: MasterKey = vault.masterKeyCurrent;\n\n let counts: SyncCounts = { wrapped: 0, alreadyCurrent: 0, unverified: 0, failed: 0 };\n\n // Sequential on purpose: the wrap PUTs are cheap, and per-device error\n // reporting stays readable.\n yield* Effect.forEach(\n devices,\n (dev) =>\n Effect.gen(function* () {\n const pubkey = sodium.fromB64(dev.devicePubkeyB64);\n const storedHmac = sodium.fromB64(dev.inviteHmacB64);\n\n // Brute-force the local invite list — at typical org sizes (<<100\n // pending invites) this is negligible. Constant-time compare prevents\n // timing-side-channel leakage of which invite matched.\n const matchedInvite = knownInvites.find((inv) =>\n sodium.constantTimeEqual(sodium.hmacInviteBinding(inv.code, pubkey), storedHmac),\n );\n\n if (!matchedInvite) {\n counts = { ...counts, unverified: counts.unverified + 1 };\n return yield* out.error(\n `device ${dev.deviceId}: HMAC doesn't match any locally-known invite — skipping. ` +\n \"(This device joined via an invite issued from a different CLI install, or the invite has been pruned.)\",\n );\n }\n\n if (dev.wrappedKeys.some((w) => w.version === currentMaster.version)) {\n counts = { ...counts, alreadyCurrent: counts.alreadyCurrent + 1 };\n return;\n }\n\n const blob = yield* sodium.wrapMasterKey(currentMaster.key, vault.adminPrivateKey, pubkey);\n const nextWraps: WrappedKey[] = [\n ...dev.wrappedKeys.filter((w) => w.version !== currentMaster.version),\n { version: currentMaster.version, blob: sodium.toB64(blob) },\n ];\n yield* api.put(\"device wrap\", `/v1/org/encryption/devices/${encodeURIComponent(dev.deviceId)}/wraps`, {\n wraps: nextWraps,\n }).pipe(\n Effect.matchEffect({\n // A single failed PUT doesn't stop the walk — the summary (and a\n // non-zero exit from the caller) reports it.\n onFailure: (e) =>\n Effect.sync(() => {\n counts = { ...counts, failed: counts.failed + 1 };\n }).pipe(Effect.zipRight(out.error(`device wrap failed (${\"status\" in e ? e.status : \"?\"}): ${\"detail\" in e ? e.detail : String(e)}`))),\n onSuccess: () =>\n Effect.gen(function* () {\n counts = { ...counts, wrapped: counts.wrapped + 1 };\n // Local invite has served its purpose for this device; drop it\n // so the candidate set shrinks over time. The backend invite\n // row is consumed server-side by the redeem step (different\n // lifecycle).\n yield* invites.consume(matchedInvite.code).pipe(Effect.ignore);\n }),\n }),\n );\n }),\n { discard: true },\n );\n\n return counts;\n });\n\nconst summarize = (counts: SyncCounts) =>\n `wrapped=${counts.wrapped} already-current=${counts.alreadyCurrent} unverified=${counts.unverified}`;\n\n// ---------- sync ----------\n\nconst syncCommand = Command.make(\"sync\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const access = yield* VaultAccess;\n\n const vault = yield* access.getOrPrompt;\n const counts = yield* wrapCurrentKeyToAllDevices(vault);\n\n yield* out.info(`Sync complete. ${summarize(counts)}`);\n if (counts.unverified > 0) {\n yield* out.info(\"Unverified devices were left untouched — their pubkeys weren't bound to any invite code this CLI knows about.\");\n }\n if (counts.failed > 0) return yield* Effect.fail(new Aborted());\n }),\n);\n\n// ---------- key show ----------\n\nconst keyShowCommand = Command.make(\"show\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const access = yield* VaultAccess;\n const sodium = yield* Sodium;\n\n const vault = yield* access.getOrPrompt;\n yield* out.info(`Current encryption key (master_key v${vault.masterKeyCurrent.version}):`);\n yield* out.print(sodium.toB64(vault.masterKeyCurrent.key));\n yield* out.info(\"Use this value plus the org API key when configuring library clients.\");\n yield* out.info(\"If it ever leaks, run `sp org encryption key rotate` to invalidate it.\");\n }),\n);\n\n// ---------- key rotate ----------\n\nconst keyRotateCommand = Command.make(\"rotate\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const access = yield* VaultAccess;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n\n // Re-prompts even if the local cache is present: rotation needs\n // `vault_key` itself to re-encrypt the updated vault, and the cache\n // deliberately doesn't store it (see VaultAccess.unlockForRotation).\n const { vault, vaultKey } = yield* access.unlockForRotation;\n\n // Build the next vault contents: new random master_key as current,\n // previous current pushed into history. We don't change the admin keypair\n // here — rotation of `master_key` is the common case (leak response,\n // periodic), while admin-keypair rotation is a separate, much more\n // expensive flow (every device has to re-pin).\n const nextVersion = vault.masterKeyCurrent.version + 1;\n const nextVault: VaultContents = {\n adminPublicKey: vault.adminPublicKey,\n adminPrivateKey: vault.adminPrivateKey,\n masterKeyCurrent: { version: nextVersion, key: yield* sodium.generateMasterKey },\n masterKeyHistory: [...vault.masterKeyHistory, vault.masterKeyCurrent],\n };\n\n // Re-fetch salt + params from the server (rather than re-deriving) so the\n // new vault blob lines up exactly with what the server will hand out to\n // unlock callers next time.\n const cfg = yield* access.fetchConfig.pipe(Effect.flatMap(access.requireEnabled));\n\n const newBlob = yield* sodium.encryptVault(nextVault, vaultKey);\n yield* api.put(\"vault update\", \"/v1/org/encryption/vault\", {\n vaultBlobB64: sodium.toB64(newBlob),\n vaultSaltB64: cfg.vaultSaltB64,\n kdfParams: cfg.kdfParams,\n });\n\n // Update the local cache before wrapping — if the wrap step fails partway,\n // the user can re-run `sync` to catch up, and we still want subsequent\n // `notify` calls on this machine to use the new key.\n yield* vaultStore.save(nextVault);\n\n yield* out.info(`Generated master_key v${nextVersion} and updated the org vault.`);\n yield* out.info(\"Wrapping the new key to every onboarded device...\");\n const counts = yield* wrapCurrentKeyToAllDevices(nextVault);\n\n yield* out.info(`Rotation complete. ${summarize(counts)}`);\n yield* out.info(\"\");\n yield* out.info(`New encryption key (master_key v${nextVersion}):`);\n yield* out.print(sodium.toB64(nextVault.masterKeyCurrent.key));\n yield* out.info(\"Update any library clients with this new value. The previous key remains valid for decrypting historical notifications only.\");\n if (counts.unverified > 0) {\n yield* out.info(\"Unverified devices were skipped — they'll need a fresh invite redeem before they can pick up the new key.\");\n }\n if (counts.failed > 0) return yield* Effect.fail(new Aborted());\n }),\n);\n\nconst keyCommand = Command.make(\"key\").pipe(\n Command.withSubcommands([keyShowCommand, keyRotateCommand]),\n);\n\n// ---------- root ----------\n\nexport const encryptionCommand = Command.make(\"encryption\").pipe(\n Command.withSubcommands([enableCommand, statusCommand, syncCommand, keyCommand]),\n);\n","// `sp org` — organization management. Subcommands authenticate with the CLI session\n// token saved by `sp auth login` (~/.config/simplepush/auth.json), via the Api\n// service; every wire shape is Schema-validated at the boundary.\n\nimport { Args, Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Schema } from \"effect\";\n\nimport { quietOption } from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { InviteStore } from \"../services/stores.js\";\nimport { Sodium } from \"../crypto/sodium.js\";\nimport { hashInviteCode } from \"../crypto/index.js\";\nimport { formatInstant } from \"../format.js\";\nimport { encryptionCommand } from \"./org-encryption.js\";\n\n// Optional backend fields (scala Option) are ABSENT from the JSON when unset —\n// zio-json omits None on encode, it never sends `\"field\": null`.\nconst optionalString = Schema.optional(Schema.NullOr(Schema.String));\n\nconst InviteResponse = Schema.Struct({\n role: Schema.String,\n expiresAt: Schema.String,\n seatsUsed: Schema.Number,\n seatsTotal: Schema.Number,\n});\n\nconst InviteSummary = Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n email: optionalString,\n role: Schema.String,\n expiresAt: Schema.String,\n createdAt: Schema.String,\n});\n\nconst MemberSummary = Schema.Struct({\n id: Schema.String,\n name: optionalString,\n email: optionalString,\n createdAt: Schema.String,\n});\n\nconst MembersResponse = Schema.Struct({ members: Schema.Array(MemberSummary) });\nconst InvitesResponse = Schema.Struct({ invites: Schema.Array(InviteSummary) });\n\nconst ApiKeyInfoResponse = Schema.Struct({\n prefix: Schema.String,\n createdAt: Schema.String,\n lastRotatedAt: optionalString,\n});\n\nconst RotateApiKeyResponse = Schema.Struct({\n apiKey: Schema.String,\n prefix: Schema.String,\n createdAt: Schema.String,\n lastRotatedAt: optionalString,\n});\n\nconst OrgTopicSummary = Schema.Struct({\n id: Schema.String,\n value: Schema.String,\n createdAt: Schema.String,\n});\n\nconst ListOrgTopicsResponse = Schema.Struct({ topics: Schema.Array(OrgTopicSummary) });\n\nconst ListOrgTopicMembersResponse = Schema.Struct({\n members: Schema.Array(Schema.Struct({ id: Schema.String, name: optionalString, email: optionalString })),\n});\n\n// ---------- commands ----------\n\nconst nameArg = Args.text({ name: \"name\" }).pipe(\n Args.withDescription(\"Display name of the person being invited (used to address them).\"),\n);\n\nconst idArg = Args.text({ name: \"id\" }).pipe(\n Args.withDescription(\"Resource id (UUID) — copy from the matching `list` output.\"),\n);\n\nconst memberNameArg = Args.text({ name: \"name\" }).pipe(\n Args.withDescription(\"Display name of the member to remove (case-insensitive). Per-org names are unique.\"),\n);\n\nconst roleOption = Options.choice(\"role\", [\"member\", \"admin\"] as const).pipe(\n Options.withDescription(\"Role for the invitee. Members consume a seat; admins do not.\"),\n Options.withDefault(\"member\" as const),\n);\n\nconst emailOption = Options.text(\"email\").pipe(\n Options.withDescription(\"Optional contact email. Saved on the invite and propagated to user.email on redemption. Not used for sending — just a label.\"),\n Options.optional,\n);\n\nconst inviteCommand = Command.make(\n \"invite\",\n { name: nameArg, role: roleOption, email: emailOption, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const invites = yield* InviteStore;\n const sodium = yield* Sodium;\n\n const email = Option.getOrUndefined(args.email);\n // Generate the cleartext code locally — the backend only ever sees the\n // hash, so a compromised backend can't forge HMACs for a substituted\n // device pubkey at sync time. The cleartext lives only in\n // `~/.config/simplepush/invites.json` (and, briefly, in the member's app\n // at redemption).\n const code = yield* sodium.generateInviteCode;\n const codeHash = hashInviteCode(code);\n const body: Record<string, string> = { name: args.name, role: args.role, codeHash };\n if (email !== undefined) body.email = email;\n\n const payload = yield* api.postJson(\"invite\", \"/v1/org/members/invites\", InviteResponse, body);\n\n // Persist the code locally so a later `org encryption sync` can verify\n // each device's HMAC(code, pubkey) against the actual issued code. The\n // backend only stores hash(code), so without this the CLI loses the\n // ability to authenticate device pubkeys at wrap time. We persist for\n // all invites — even if encryption isn't enabled yet, it might be later,\n // and the entry harmlessly ages out at expiresAt.\n yield* invites\n .append({\n code,\n name: args.name,\n role: args.role,\n issuedAt: new Date().toISOString(),\n expiresAt: payload.expiresAt,\n })\n .pipe(\n // Don't fail the command for a local-storage hiccup — the backend\n // already created the invite, and the code below is the user-visible\n // contract. Just warn so they know encryption sync would miss this\n // one until it's re-issued.\n Effect.catchAll((err) =>\n out.error(`(warning) failed to persist invite locally for encryption sync: ${err instanceof Error ? err.message : String(err)}`),\n ),\n );\n\n yield* out.info(`Invite created for ${args.name}${email ? ` <${email}>` : \"\"} (role: ${payload.role}).`);\n yield* out.info(`Expires: ${formatInstant(payload.expiresAt)}`);\n if (payload.role === \"member\") yield* out.info(`Seats used: ${payload.seatsUsed} / ${payload.seatsTotal}`);\n // The code is shown exactly once — print it on stdout so callers can pipe it.\n yield* out.print(`Login code: ${code}`);\n }),\n);\n\nconst membersListCommand = Command.make(\"list\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const { members } = yield* api.getJson(\"list\", \"/v1/org/members\", MembersResponse);\n if (members.length === 0) return yield* out.info(\"no active members\");\n yield* Effect.forEach(members, (m) =>\n out.print(`${m.name ?? \"(unnamed)\"}\\t${m.email ?? \"-\"}\\t${formatInstant(m.createdAt)}`),\n );\n }),\n);\n\n// `--yes` gates the destructive call. Without it the CLI prints a warning of\n// exactly what gets deleted server-side and exits non-zero without touching\n// the backend, so accidental `sp org members remove alice` (e.g. tab completion\n// nudging the wrong name) doesn't wipe data. Scripts pass `--yes` to confirm.\nconst yesOption = Options.boolean(\"yes\").pipe(\n Options.withAlias(\"y\"),\n Options.withDescription(\"Confirm the removal. Without this flag the command only prints a warning and exits.\"),\n);\n\n// Resolve name → uuid via the list endpoint. The backend keys deletes on UUID\n// (stable across renames), but per-org names are case-insensitively unique by\n// DB constraint so a name lookup is unambiguous. Two requests per op is\n// acceptable for a human-driven CLI.\nconst resolveMemberByName = (name: string) =>\n Effect.gen(function* () {\n const api = yield* Api;\n const { members } = yield* api.getJson(\"resolve member\", \"/v1/org/members\", MembersResponse);\n const target = name.trim().toLowerCase();\n const match = members.find((m) => (m.name ?? \"\").toLowerCase() === target);\n if (!match) {\n return yield* Effect.fail(\n new UserError({ message: `no member named '${name}' — run \\`simplepush org members list\\` to see members` }),\n );\n }\n return match;\n });\n\nconst membersRemoveCommand = Command.make(\n \"remove\",\n { name: memberNameArg, yes: yesOption, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const match = yield* resolveMemberByName(args.name).pipe(\n Effect.mapError((e) => (e._tag === \"UserError\" ? new UserError({ message: `no member named '${args.name}' in this org` }) : e)),\n );\n\n yield* out.info(`Removing '${match.name ?? args.name}' permanently deletes all of their associated data. Only event history is retained.`);\n if (!args.yes) {\n yield* out.info(\"Re-run with --yes (or -y) to confirm.\");\n return yield* Effect.fail(new Aborted());\n }\n\n yield* api.delete(\"remove\", `/v1/org/members/${encodeURIComponent(match.id)}`);\n yield* out.info(`removed member ${match.name ?? args.name}`);\n }),\n);\n\nconst invitesListCommand = Command.make(\"list\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const { invites } = yield* api.getJson(\"list\", \"/v1/org/members/invites\", InvitesResponse);\n if (invites.length === 0) return yield* out.info(\"no pending invites\");\n yield* Effect.forEach(invites, (inv) =>\n out.print(`${inv.id}\\t${inv.name}\\t${inv.email ?? \"-\"}\\t${inv.role}\\texpires ${formatInstant(inv.expiresAt)}`),\n );\n }),\n);\n\nconst invitesRevokeCommand = Command.make(\"revoke\", { id: idArg, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n yield* api.delete(\"revoke\", `/v1/org/members/invites/${encodeURIComponent(args.id)}`);\n yield* out.info(`revoked invite ${args.id}`);\n }),\n);\n\n// `members` carries: invite, list, remove. The shorter `sp org members` -> list is\n// usually what people type; we pull the `members list` subcommand alongside it for\n// explicit usage too.\nconst membersCommand = Command.make(\"members\").pipe(\n Command.withSubcommands([inviteCommand, membersListCommand, membersRemoveCommand]),\n);\n\n// `invites` carries: list, revoke. Mirrors GET / DELETE on /v1/org/members/invites.\nconst invitesCommand = Command.make(\"invites\").pipe(\n Command.withSubcommands([invitesListCommand, invitesRevokeCommand]),\n);\n\n// ---------- api-key ----------\n\nconst apiKeyInfoCommand = Command.make(\"info\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const payload = yield* api.getJson(\"info\", \"/v1/org/api-key\", ApiKeyInfoResponse);\n yield* out.print(`Prefix: ${payload.prefix}…`);\n yield* out.print(`Created: ${formatInstant(payload.createdAt)}`);\n yield* out.print(`Last rotated: ${payload.lastRotatedAt ? formatInstant(payload.lastRotatedAt) : \"never\"}`);\n yield* out.info(\"(plaintext is unrecoverable; run `api-key rotate` to surface a new key)\");\n }),\n);\n\nconst apiKeyRotateCommand = Command.make(\"rotate\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const payload = yield* api.postJson(\"rotate\", \"/v1/org/api-key/rotate\", RotateApiKeyResponse);\n yield* out.info(\"The previous key (if any) is now invalid.\");\n yield* out.print(\"\\nOrganization API key (shown once, copy now):\");\n yield* out.print(` ${payload.apiKey}`);\n }),\n);\n\nconst apiKeyCommand = Command.make(\"api-key\").pipe(\n Command.withSubcommands([apiKeyInfoCommand, apiKeyRotateCommand]),\n);\n\n// ---------- topics ----------\n//\n// Org topics are admin-managed channels: admins create them, assign members to them,\n// and then send notifications addressed to the topic via the API key. Members do not\n// self-subscribe — assignment is one-way from the admin side, mirroring the existing\n// invite flow.\n\nconst topicValueArg = Args.text({ name: \"value\" }).pipe(\n Args.withDescription(\"Topic value (no whitespace, ≤ 255 chars). Case-insensitive uniqueness within the org.\"),\n);\n\n// We resolve topic by `value` everywhere on the CLI because admins type names\n// they recognize. The HTTP API itself keys on the topic id (stable across\n// renames if we ever add renames).\nconst resolveOrgTopicIdByValue = (value: string) =>\n Effect.gen(function* () {\n const api = yield* Api;\n const { topics } = yield* api.getJson(\"list org topics\", \"/v1/org/topics\", ListOrgTopicsResponse);\n const target = value.trim().toLowerCase();\n const match = topics.find((t) => t.value.toLowerCase() === target);\n if (!match) {\n return yield* Effect.fail(\n new UserError({ message: `no org topic '${value}' — run \\`simplepush org topics list\\` to see available topics` }),\n );\n }\n return match.id;\n });\n\nconst topicsCreateCommand = Command.make(\"create\", { value: topicValueArg, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const payload = yield* api.postJson(\"create\", \"/v1/org/topics\", OrgTopicSummary, { value: args.value });\n yield* out.info(`Created org topic '${payload.value}'.`);\n }),\n);\n\nconst topicsListCommand = Command.make(\"list\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const { topics } = yield* api.getJson(\"list\", \"/v1/org/topics\", ListOrgTopicsResponse);\n if (topics.length === 0) return yield* out.info(\"no org topics\");\n yield* Effect.forEach(topics, (t) => out.print(`${t.value}\\tcreated ${formatInstant(t.createdAt)}`));\n }),\n);\n\nconst topicsDeleteCommand = Command.make(\"delete\", { value: topicValueArg, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.value);\n yield* api.delete(\"delete\", `/v1/org/topics/${encodeURIComponent(orgTopicId)}`);\n yield* out.info(`Deleted org topic '${args.value}'.`);\n }),\n);\n\nconst topicValueAssignArg = Args.text({ name: \"topic\" }).pipe(\n Args.withDescription(\"Org topic value (from `topics list`).\"),\n);\n\nconst memberNameAssignArg = Args.text({ name: \"member\" }).pipe(\n Args.withDescription(\"Member display name (case-insensitive, from `members list`).\"),\n);\n\nconst topicsAssignCommand = Command.make(\n \"assign\",\n { topic: topicValueAssignArg, member: memberNameAssignArg, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.topic);\n const member = yield* resolveMemberByName(args.member);\n yield* api.put(\"assign\", `/v1/org/topics/${encodeURIComponent(orgTopicId)}/members/${encodeURIComponent(member.id)}`);\n yield* out.info(`Assigned ${args.member} to org topic '${args.topic}'.`);\n }),\n);\n\nconst topicsUnassignCommand = Command.make(\n \"unassign\",\n { topic: topicValueAssignArg, member: memberNameAssignArg, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.topic);\n const member = yield* resolveMemberByName(args.member);\n yield* api.delete(\"unassign\", `/v1/org/topics/${encodeURIComponent(orgTopicId)}/members/${encodeURIComponent(member.id)}`);\n yield* out.info(`Unassigned ${args.member} from org topic '${args.topic}'.`);\n }),\n);\n\nconst topicsMembersCommand = Command.make(\n \"members\",\n { topic: topicValueAssignArg, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.topic);\n const { members } = yield* api.getJson(\n \"members\",\n `/v1/org/topics/${encodeURIComponent(orgTopicId)}/members`,\n ListOrgTopicMembersResponse,\n );\n if (members.length === 0) return yield* out.info(\"no members assigned\");\n yield* Effect.forEach(members, (m) => out.print(`${m.name ?? \"(unnamed)\"}\\t${m.email ?? \"-\"}`));\n }),\n);\n\nconst topicsCommand = Command.make(\"topics\").pipe(\n Command.withSubcommands([\n topicsCreateCommand,\n topicsListCommand,\n topicsDeleteCommand,\n topicsAssignCommand,\n topicsUnassignCommand,\n topicsMembersCommand,\n ]),\n);\n\nexport const orgCommand = Command.make(\"org\").pipe(\n Command.withSubcommands([membersCommand, invitesCommand, apiKeyCommand, topicsCommand, encryptionCommand]),\n);\n","import { basename, extname } from \"node:path\";\n\nimport { FileSystem } from \"@effect/platform\";\nimport { Effect } from \"effect\";\nimport {\n uploadFileAttachments,\n type AttachmentLifecycleContext,\n type CreatedAttachment,\n type FileAttachment,\n type PreparedFile,\n} from \"@simplepush/sdk\";\n\nimport { Api } from \"./services/api.js\";\nimport { bearerToken } from \"./services/stores.js\";\nimport { sdkCall } from \"./services/sdk.js\";\n\n/** Read each `--file` path into a `FileAttachment` the SDK can upload: bytes +\n * basename + a best-effort content type guessed from the extension (the SDK\n * defaults to application/octet-stream when undefined). */\nexport const buildFiles = (paths: ReadonlyArray<string>) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n return yield* Effect.forEach(paths, (p) =>\n Effect.map(fs.readFile(p), (data): FileAttachment => {\n const contentType = guessContentType(p);\n return {\n filename: basename(p),\n data,\n ...(contentType !== undefined ? { contentType } : {}),\n };\n }),\n );\n });\n\n/** Drive the presign -> PUT -> complete lifecycle for an org send's prepared\n * attachments over the CLI bearer session (`/v1/org/attachments/...` — the\n * bearer-auth'd variants of the attachment lifecycle endpoints). No-op when the\n * send carried no files. Per-file failures are marked `failed` server-side and\n * skipped (the SDK's best-effort semantics) — the task/subtask itself stands. */\nexport const uploadOrgAttachments = (prepared: PreparedFile[], created: CreatedAttachment[] | undefined) =>\n Effect.gen(function* () {\n if (prepared.length === 0) return;\n const api = yield* Api;\n const auth = yield* api.session;\n const ctx: AttachmentLifecycleContext = {\n // Trailing slash so the lifecycle's relative paths append to the base.\n baseUrl: new URL(`${auth.baseUrl.replace(/\\/+$/, \"\")}/`),\n authHeaders: { Authorization: `Bearer ${bearerToken(auth)}` },\n basePath: \"v1/org/attachments\",\n };\n yield* sdkCall(\"upload attachments\", () => uploadFileAttachments(ctx, prepared, created ?? []));\n });\n\n// Minimal extension -> MIME map. The content type drives receiver-side\n// rendering (image/video inline vs a generic file), so cover the common media\n// kinds; anything else falls through to the SDK's octet-stream default.\nconst CONTENT_TYPES: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n heic: \"image/heic\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n txt: \"text/plain\",\n json: \"application/json\",\n csv: \"text/csv\",\n zip: \"application/zip\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n mp3: \"audio/mpeg\",\n m4a: \"audio/mp4\",\n wav: \"audio/wav\",\n};\n\nfunction guessContentType(path: string): string | undefined {\n const ext = extname(path).slice(1).toLowerCase();\n return CONTENT_TYPES[ext];\n}\n","import { Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Schema, Stream } from \"effect\";\nimport {\n type Input,\n type SendOptions,\n type OrgSendTarget,\n type CreateTaskResponse,\n type Task,\n buildOrgTaskRequest,\n isTaskGroupResponse,\n prepareFileAttachments,\n} from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n requireApiToken,\n topicOption,\n willEncrypt,\n type PasswordFlag,\n} from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { acquireClient, sdkCall, sdkStream } from \"../services/sdk.js\";\nimport { formatSent } from \"../collect-output.js\";\nimport { buildFiles, uploadOrgAttachments } from \"../files.js\";\nimport { buildInputs } from \"../input-spec.js\";\n\nconst titleOption = Options.text(\"title\").pipe(\n Options.withDescription(\"Task title.\"),\n Options.optional,\n);\n\nconst contentOption = Options.text(\"content\").pipe(\n Options.withDescription(\"Task description / body content.\"),\n Options.optional,\n);\n\nconst tagOption = Options.text(\"tag\").pipe(\n Options.withDescription(\"Tag the task for receiver-side filtering. Defaults to $SP_TAG.\"),\n Options.optional,\n);\n\nconst repeatedText = (name: string, alias: string | undefined, description: string) => {\n const base = Options.text(name).pipe(\n Options.withDescription(description),\n Options.repeated,\n );\n return alias ? Options.withAlias(alias)(base) : base;\n};\n\nconst textInput = repeatedText(\n \"text-input\",\n undefined,\n \"Add a text input. Format: `description[;key=value...]`. Settings: `required=true|false` (default true), `defaultValue=...`. Repeatable.\",\n);\nconst choiceInput = repeatedText(\n \"choice-input\",\n \"c\",\n \"Add a choice input. Format: `[description;]options[;key=value...]`, options comma-separated. Settings: `required=true|false`, `multi=true|false` (allow picking more than one option, default false), `minSelections=<int>`/`maxSelections=<int>` (only with multi). Use `\\\\;` for a literal semicolon. Repeatable.\",\n);\nconst actionInput = repeatedText(\n \"action-input\",\n \"a\",\n \"Add an actions input (buttons the recipient taps, e.g. Accept/Deny). Format: `[description;]key=Label[:style],...[;required=true|false]`, actions comma-separated; style is default|primary|destructive. Use `\\\\,` for a literal comma in a label. Repeatable.\",\n);\nconst sliderInput = repeatedText(\n \"slider-input\",\n \"s\",\n \"Add a slider input (the recipient picks a number on a scale). Format: `[description;]min=0;max=14;step=0.1;unit=pH;default=7`. `min`/`max` are required; `step`/`unit`/`default` optional. Repeatable.\",\n);\nconst photoInput = repeatedText(\n \"photo-input\",\n undefined,\n \"Add a photo input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\nconst voiceRecordingInput = repeatedText(\n \"voice-recording-input\",\n undefined,\n \"Add a voice recording input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\nconst fileInput = repeatedText(\n \"file-input\",\n undefined,\n \"Add a file upload input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\nconst locationInput = repeatedText(\n \"location-input\",\n undefined,\n \"Add a location input (the recipient shares their device GPS position from the app). Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\n\nconst linkOption = repeatedText(\n \"link\",\n \"l\",\n \"Attach a remote URL (a link attachment). Repeatable. For local files use --file.\",\n);\n\nconst fileOption = repeatedText(\n \"file\",\n \"f\",\n \"Attach a local file, uploaded as a file attachment (encrypted under the send's key — topic password or org master key — when the send is encrypted). Repeatable.\",\n);\n\nconst submitOption = Options.boolean(\"submit\").pipe(\n Options.withDescription(\"Require the recipient to explicitly submit the task. Without this, it auto-completes once the required inputs are filled.\"),\n);\n\nconst waitOption = Options.boolean(\"wait\").pipe(\n Options.withDescription(\"Block until the task is completed; print the result to stdout. Requires exactly one input on the request.\"),\n);\n\n// `--reply <mode>` opts recipients into the composer below the rendered\n// task. Wire values match the backend's ReplyMode strings. Absent =\n// no composer (default).\nconst replyOption = Options.choice(\"reply\", [\"one-shot\", \"sticky\", \"one-time-per-user\"] as const).pipe(\n Options.withDescription(\n \"Show a reply composer on the recipient's task: 'one-shot' (first reply wins, closes the slot), 'sticky' (open indefinitely), 'one-time-per-user' (one reply per user).\",\n ),\n Options.optional,\n);\n\n// Org targeting (mutually exclusive with each other and with -k/--topic, the\n// personal-topic flag). When any is set the task goes out via the CLI bearer\n// session + org-vault encryption to /v1/org/tasks/json, like `sp notify`.\nconst memberOption = Options.text(\"member\").pipe(\n Options.withAlias(\"m\"),\n Options.withDescription(\"Send to a single org member by display name (case-insensitive). Org send; mutually exclusive with --broadcast, --org-topic, and -k/--topic.\"),\n Options.optional,\n);\n\nconst broadcastOption = Options.boolean(\"broadcast\").pipe(\n Options.withAlias(\"b\"),\n Options.withDescription(\"Send to every member of the org. Org send; mutually exclusive with --member, --org-topic, and -k/--topic.\"),\n);\n\nconst orgTopicOption = Options.text(\"org-topic\").pipe(\n Options.withAlias(\"o\"),\n Options.withDescription(\"Send to an org topic by value (from `sp org topics list`). Org send; mutually exclusive with --member, --broadcast, and -k/--topic (which is the personal topic).\"),\n Options.optional,\n);\n\nconst noEncryptOption = Options.boolean(\"no-encrypt\").pipe(\n Options.withDescription(\"For org sends: send fields in plaintext even when the org vault is unlocked.\"),\n);\n\nconst markdownOption = Options.boolean(\"markdown\").pipe(\n Options.withDescription(\"Render the task body as Markdown on the recipient's device (sets contentFormat=markdown).\"),\n);\n\nconst sharedOption = Options.boolean(\"shared\").pipe(\n Options.withDescription(\n \"Shared mode: ONE task all recipients see and answer together (user A's input is visible to user B). Default (without this flag) is independent mode: every recipient gets their own task instance under a group.\",\n ),\n);\n\n// stdout shape for a send (not `--wait`): `text` prints the bare id (default,\n// human/script), `json` prints a machine-readable `sent` line that\n// `sp collect` consumes to know the group + members + resume point.\nconst formatOption = Options.choice(\"format\", [\"text\", \"json\"] as const).pipe(\n Options.withDescription(\"stdout format for a send: `text` (the bare id, default) or `json` (a `sent` line piped to `sp collect`).\"),\n Options.withDefault(\"text\"),\n);\n\nexport const taskCommand = Command.make(\n \"task\",\n {\n title: titleOption,\n content: contentOption,\n tag: tagOption,\n \"text-input\": textInput,\n \"choice-input\": choiceInput,\n \"action-input\": actionInput,\n \"slider-input\": sliderInput,\n \"photo-input\": photoInput,\n \"voice-recording-input\": voiceRecordingInput,\n \"file-input\": fileInput,\n \"location-input\": locationInput,\n link: linkOption,\n file: fileOption,\n submit: submitOption,\n wait: waitOption,\n reply: replyOption,\n member: memberOption,\n broadcast: broadcastOption,\n \"org-topic\": orgTopicOption,\n \"no-encrypt\": noEncryptOption,\n markdown: markdownOption,\n shared: sharedOption,\n format: formatOption,\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const memberName = Option.getOrUndefined(args.member);\n const orgTopicName = Option.getOrUndefined(args[\"org-topic\"]);\n const topic = args.topic[0];\n // At most one of: org member, org broadcast, org topic, or a personal topic.\n // ZERO targets on a personal send = a note-to-self (your own devices).\n const targetCount = [memberName !== undefined, args.broadcast, orgTopicName !== undefined, topic !== undefined].filter(Boolean).length;\n if (targetCount > 1) {\n return yield* Effect.fail(\n new UserError({ message: \"pass at most one target: -m <member> | -b (broadcast) | --org-topic <value> | -t <topic> (omit all for a self-send)\" }),\n );\n }\n\n // Shared task content (plaintext here; the org path encrypts under the vault).\n const inputs = yield* Effect.try({\n try: () => buildInputs(args),\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n if (inputs.length === 0 && args.wait) {\n yield* out.warn(\"--wait requested but no inputs were defined; the server will never produce a TaskCompleted event\");\n }\n const tag = Option.getOrElse(args.tag, () => process.env.SP_TAG ?? \"\");\n const title = Option.getOrUndefined(args.title);\n const content = Option.getOrUndefined(args.content);\n\n // Org send (member / broadcast / org topic): CLI bearer session + org-vault\n // field encryption, POST to /v1/org/tasks/json. Mirrors `sp notify`.\n if (memberName !== undefined || args.broadcast || orgTopicName !== undefined) {\n return yield* sendOrgTask({\n member: memberName,\n broadcast: args.broadcast,\n orgTopic: orgTopicName,\n tag,\n title,\n content,\n inputs,\n links: [...args.link],\n files: [...args.file],\n autoCommit: !args.submit,\n reply: Option.getOrUndefined(args.reply),\n markdown: args.markdown,\n noEncrypt: args[\"no-encrypt\"],\n wait: args.wait,\n shared: args.shared,\n format: args.format,\n });\n }\n\n // Personal send: a topic (password@topic path) OR a note-to-self (no topic).\n const passwords = args.password as ReadonlyArray<PasswordFlag>;\n // The SDK auto-encrypts a topic send when a `password@topic` pair matches\n // the topic; a note-to-self encrypts under the account default password (a\n // bare --password). Warn about the Argon2 cost in either case.\n const encrypting = willEncrypt(passwords, topic);\n\n // Read each --file off disk into a FileAttachment; the SDK encrypts the\n // bytes (when the topic has a password) and drives the upload lifecycle.\n const files = yield* buildFiles(args.file);\n\n // A client always carries a credential (matching the Python SDK) — even\n // though topic sends themselves go out without it on the wire.\n const apiToken = yield* requireApiToken(args[\"api-token\"]);\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireClient({\n baseUrl: args[\"base-url\"],\n apiToken,\n passwords: [...passwords],\n });\n\n // Encryption is driven by the configured `password@topic` pair for this\n // topic; the SDK encrypts each field under the topic key (salt = topic value).\n if (encrypting) yield* out.info(\"encrypting outgoing task (Argon2id, this takes a moment)\");\n const baseOpts = {\n ...(tag ? { tag } : {}),\n ...(title !== undefined ? { title } : {}),\n ...(content !== undefined ? { content } : {}),\n inputs,\n links: [...args.link],\n ...(files.length > 0 ? { files } : {}),\n autoCommit: !args.submit,\n ...(Option.isSome(args.reply) ? { reply: args.reply.value } : {}),\n ...(args.markdown ? { contentFormat: \"markdown\" as const } : {}),\n };\n\n if (topic === undefined) {\n // Note to self: no topic → the task goes to your own devices, returned\n // as a single Task (never a group — there is one recipient, you).\n const response = yield* sdkCall(\"task send\", () => client.sendTask(baseOpts));\n yield* out.info(`self-send task created: ${response.taskId}`);\n yield* out.info(`append token: ${response.appendToken}`);\n if (!args.wait) {\n if (args.format === \"json\") yield* out.print(formatSent(undefined, response.createdAt, [{ id: response.taskId, kind: \"task\", recipient: null }]));\n else yield* out.print(response.taskId);\n return;\n }\n yield* out.info(`waiting for completion of task ${response.taskId}`);\n return yield* waitForFirstCompletion([response]);\n }\n\n const sendOpts = { ...baseOpts, topic };\n\n if (args.shared) {\n // Single shared task: one id/token, all recipients share state.\n const response = yield* sdkCall(\"task send\", () => client.sendTask({ ...sendOpts, shared: true }));\n yield* out.info(`task created: ${response.taskId}`);\n yield* out.info(`append token: ${response.appendToken}`);\n if (!args.wait) {\n if (args.format === \"json\") yield* out.print(formatSent(undefined, response.createdAt, [{ id: response.taskId, kind: \"task\", recipient: null }]));\n else yield* out.print(response.taskId);\n return;\n }\n yield* out.info(`waiting for completion of task ${response.taskId}`);\n return yield* waitForFirstCompletion([response]);\n }\n\n // Independent mode (default): one task instance per recipient under a group.\n const group = yield* sdkCall(\"task send\", () => client.sendTask(sendOpts));\n yield* out.info(`task group created: ${group.groupId} (${group.instances.length} recipient${group.instances.length === 1 ? \"\" : \"s\"})`);\n yield* out.info(`group append token: ${group.appendToken}`);\n yield* Effect.forEach(group.instances, (inst) => {\n const who = inst.recipient ? `${inst.recipient.publicId}${inst.recipient.name ? ` (${inst.recipient.name})` : \"\"}` : \"unknown\";\n return out.info(`instance: ${inst.taskId} -> ${who} append token: ${inst.appendToken}`);\n });\n if (group.instances.length === 0) yield* out.warn(\"the topic has no recipients — the group is empty\");\n\n if (!args.wait) {\n if (args.format === \"json\") {\n // Machine-readable handle for `sp collect`: the group + its members\n // (taskId + recipient) + createdAt (the collect resume point).\n yield* out.print(\n formatSent(\n group.groupId,\n group.createdAt,\n group.instances.map((i) => ({\n id: i.taskId,\n kind: \"task\" as const,\n recipient: i.recipient ? { publicId: i.recipient.publicId, name: i.recipient.name ?? null } : null,\n })),\n ),\n );\n } else {\n // stdout contract: the primary handle. grptsk_-prefixed, so scripts\n // can tell it apart from a plain tsk_ id.\n yield* out.print(group.groupId);\n }\n return;\n }\n\n if (group.instances.length === 0) {\n // --wait promises a completion value on stdout; with zero instances\n // none can ever arrive, so this is a failure, not a quiet success.\n yield* out.warn(\"--wait requested but the group has no instances; nothing will complete\");\n return yield* Effect.fail(new Aborted());\n }\n yield* out.info(`waiting for the first completion across ${group.instances.length} instance(s) of ${group.groupId}`);\n yield* waitForFirstCompletion(group.instances);\n }),\n );\n }),\n);\n\n/** Merge every instance's input stream and take the FIRST completed answer\n * (mirrors the backend's curl Wait semantics: one answer from any recipient).\n * Per instance, a `taskDeleted` benignly ends that sub-stream. No completion\n * anywhere is a FAILURE (exit 1, empty stdout); a real stream error (auth,\n * transport) fails the merge and is surfaced as such. */\nconst waitForFirstCompletion = (tasks: readonly Task[]) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n\n const completions = tasks.map((t) =>\n sdkStream(\"task wait stream\", (signal) => t.inputs({ replay: true, signal })).pipe(\n Stream.takeWhile((ev) => ev.kind !== \"taskDeleted\"),\n Stream.filter((ev) => ev.kind === \"taskCompleted\"),\n Stream.take(1),\n ),\n );\n\n const first = yield* Stream.mergeAll(completions, { concurrency: \"unbounded\" }).pipe(\n Stream.runHead,\n Effect.mapError((e) => {\n const msg = e.cause instanceof Error ? e.cause.message : String(e.cause);\n return new UserError({ message: `stream failed while waiting: ${msg}` });\n }),\n );\n\n if (Option.isNone(first)) {\n yield* out.warn(\"every instance ended (deleted or stream closed) before a completion\");\n return yield* Effect.fail(new Aborted());\n }\n\n const ev = first.value;\n const uploads = ev.kind === \"taskCompleted\" ? ev.uploads : [];\n const single = uploads.length === 1 ? uploads[0] : undefined;\n // A multi-choice answer is a list of values; emit them comma-joined as a\n // plain string (mirrors the single-choice `value` line), defined values only.\n const value =\n single && (single.kind === \"text\" || single.kind === \"choice\") ? single.value\n : single && single.kind === \"action\" ? single.key\n : single && single.kind === \"multiChoice\" ? (single.values ?? []).filter((v): v is string => typeof v === \"string\").join(\", \")\n : undefined;\n yield* out.print(typeof value === \"string\" ? value : JSON.stringify(uploads));\n });\n\n/** Send a task to org recipients (a member, a broadcast, or an org topic) over\n * the CLI bearer session, encrypting each user-visible field under the current\n * org master_key when the vault is unlocked. Mirrors `sp notify` and the SDK's\n * OrgClient.sendTask field set (tag/title/content/links + each input's\n * description/default/options). File attachments are not supported (the bearer\n * session can't drive uploads). */\nconst sendOrgTask = (params: {\n member?: string;\n broadcast: boolean;\n orgTopic?: string;\n tag: string;\n title?: string;\n content?: string;\n inputs: Input[];\n links: string[];\n files: string[];\n autoCommit: boolean;\n reply?: \"one-shot\" | \"sticky\" | \"one-time-per-user\";\n markdown: boolean;\n noEncrypt: boolean;\n wait: boolean;\n shared: boolean;\n format: \"text\" | \"json\";\n}) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n const access = yield* VaultAccess;\n\n // Backend requires content OR at least one input.\n if (params.content === undefined && params.inputs.length === 0) {\n return yield* Effect.fail(new UserError({ message: \"an org task needs --content or at least one input\" }));\n }\n if (params.wait) yield* out.warn(\"--wait isn't supported on org sends; ignoring\");\n\n const vault = yield* access.forSendOrPlaintext(params.noEncrypt);\n\n // Reuse the SDK's field encryption + body shape (the same `OrgClient.sendTask`\n // produces) — we only own the bearer POST, no hand-rolled crypto.\n const target: OrgSendTarget =\n params.orgTopic !== undefined ? { topic: params.orgTopic }\n : params.member !== undefined ? { member: params.member }\n : { broadcast: true };\n const opts: SendOptions = {\n ...(params.tag ? { tag: params.tag } : {}),\n ...(params.title !== undefined ? { title: params.title } : {}),\n ...(params.content !== undefined ? { content: params.content } : {}),\n inputs: params.inputs,\n links: params.links,\n autoCommit: params.autoCommit,\n ...(params.reply !== undefined ? { reply: params.reply } : {}),\n ...(params.markdown ? { contentFormat: \"markdown\" as const } : {}),\n ...(params.shared ? { shared: true } : {}),\n };\n const masterKey = vault\n ? { key: vault.masterKeyCurrent.key, version: vault.masterKeyCurrent.version }\n : undefined;\n // Files ride the same lifecycle as an Api-Key SDK send: metadata in the\n // create body (bytes encrypted under the org master key when the vault is\n // unlocked), then presign -> PUT -> complete against the org bearer\n // attachment endpoints.\n const fileAttachments = yield* buildFiles(params.files);\n const prepared = yield* sdkCall(\"prepare attachments\", () => prepareFileAttachments(fileAttachments, masterKey?.key));\n const body = yield* sdkCall(\"build task request\", () =>\n buildOrgTaskRequest(target, opts, masterKey, prepared.map((p) => p.meta)),\n );\n\n // The endpoint's CreateTaskJsonRequest codec flattens (decodes the bare\n // CreateTaskData), so the body goes on the wire UNWRAPPED — no { data: ... }.\n // The response union is SDK-owned; its type guard discriminates.\n const payload = (yield* api.postJson(\"task\", \"/v1/org/tasks/json\", Schema.Unknown, body)) as CreateTaskResponse;\n yield* uploadOrgAttachments(prepared, payload.attachments);\n const enc = vault ? ` (encrypted under master_key v${vault.masterKeyCurrent.version})` : \" (plaintext)\";\n if (isTaskGroupResponse(payload)) {\n yield* out.info(`Org task group sent${enc}.`);\n yield* out.info(`Group: ${payload.groupId} (${payload.instances.length} recipient${payload.instances.length === 1 ? \"\" : \"s\"})`);\n yield* out.info(`Append: ${payload.groupAppendToken}`);\n yield* Effect.forEach(payload.instances, (inst) => {\n const who = `${inst.recipient.publicId}${inst.recipient.name ? ` (${inst.recipient.name})` : \"\"}`;\n return out.info(`Instance: ${inst.taskId} -> ${who} append token: ${inst.appendToken}`);\n });\n if (payload.instances.length === 0) yield* out.warn(\"the target has no recipients — the group is empty\");\n if (params.format === \"json\") {\n yield* out.print(\n formatSent(\n payload.groupId,\n payload.createdAt,\n payload.instances.map((i) => ({ id: i.taskId, kind: \"task\" as const, recipient: { publicId: i.recipient.publicId, name: i.recipient.name ?? null } })),\n ),\n );\n } else {\n yield* out.print(payload.groupId);\n }\n } else {\n yield* out.info(`Org task sent${enc}.`);\n yield* out.info(`Id: ${payload.taskId}`);\n yield* out.info(`Append: ${payload.appendToken}`);\n if (params.format === \"json\") yield* out.print(formatSent(undefined, payload.createdAt, [{ id: payload.taskId, kind: \"task\", recipient: null }]));\n else yield* out.print(payload.taskId);\n }\n });\n\n","import { Command, Options } from \"@effect/cli\";\nimport { Effect, Option } from \"effect\";\nimport {\n type SendSubtaskOptions,\n type CreateSubtaskResponse,\n buildOrgSubtaskRequest,\n isSubtaskGroupResponse,\n prepareFileAttachments,\n} from \"@simplepush/sdk\";\nimport { Schema } from \"effect\";\n\nimport { buildFiles, uploadOrgAttachments } from \"../files.js\";\nimport { buildInputs } from \"../input-spec.js\";\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n requireApiToken,\n topicOption,\n willEncrypt,\n} from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { acquireClient, sdkCall } from \"../services/sdk.js\";\n\n// A subtask attaches to an existing task's chain via its signed `appendToken`\n// (printed by `sp task`) — not a task id. It carries no targeting: it inherits\n// the parent's recipients and must match the parent's encryption.\nconst appendTokenOption = Options.text(\"append-token\").pipe(\n Options.withDescription(\"The parent task's append token (the `appendToken` printed by `sp task`).\"),\n);\n\nconst titleOption = Options.text(\"title\").pipe(\n Options.withDescription(\"Subtask title.\"),\n Options.optional,\n);\n\nconst contentOption = Options.text(\"content\").pipe(\n Options.withDescription(\"Subtask description / body content.\"),\n Options.optional,\n);\n\nconst linkOption = Options.text(\"link\").pipe(\n Options.withAlias(\"l\"),\n Options.withDescription(\"Attach a remote URL (a link attachment). Repeatable. For local files use --file.\"),\n Options.repeated,\n);\n\nconst fileOption = Options.text(\"file\").pipe(\n Options.withAlias(\"f\"),\n Options.withDescription(\"Attach a local file, uploaded as a file attachment (encrypted under the parent chain's key — topic password or org master key — when the chain is encrypted). Repeatable.\"),\n Options.repeated,\n);\n\nconst textInput = Options.text(\"text-input\").pipe(\n Options.withDescription(\n \"Add a text input. Format: `description[;key=value...]`. Settings: `required=true|false` (default true), `defaultValue=...`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst actionInput = Options.text(\"action-input\").pipe(\n Options.withAlias(\"a\"),\n Options.withDescription(\n \"Add an actions input (buttons the recipient taps, e.g. Accept/Deny). Format: `[description;]key=Label[:style],...[;required=true|false]`, actions comma-separated; style is default|primary|destructive. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst choiceInput = Options.text(\"choice-input\").pipe(\n Options.withAlias(\"c\"),\n Options.withDescription(\n \"Add a choice input. Format: `[description;]options[;key=value...]`, options comma-separated. Settings: `required=true|false`, `multi=true|false` (allow picking more than one option, default false), `minSelections=<int>`/`maxSelections=<int>` (only with multi). Use `\\\\;` for a literal semicolon. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst sliderInput = Options.text(\"slider-input\").pipe(\n Options.withAlias(\"s\"),\n Options.withDescription(\n \"Add a slider input (the recipient picks a number on a scale). Format: `[description;]min=0;max=14;step=0.1;unit=pH;default=7`. `min`/`max` required; `step`/`unit`/`default` optional. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst photoInput = Options.text(\"photo-input\").pipe(\n Options.withDescription(\n \"Add a photo input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst voiceRecordingInput = Options.text(\"voice-recording-input\").pipe(\n Options.withDescription(\n \"Add a voice recording input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst fileInput = Options.text(\"file-input\").pipe(\n Options.withDescription(\n \"Add a file upload input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst locationInput = Options.text(\"location-input\").pipe(\n Options.withDescription(\n \"Add a location input (the recipient shares their device GPS position from the app). Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst submitOption = Options.boolean(\"submit\").pipe(\n Options.withDescription(\"Require the recipient to explicitly submit the subtask. Without this, it auto-completes once the required inputs are filled.\"),\n);\n\nconst markdownOption = Options.boolean(\"markdown\").pipe(\n Options.withDescription(\"Render the subtask body as Markdown on the recipient's device (sets contentFormat=markdown).\"),\n);\n\nconst noEncryptOption = Options.boolean(\"no-encrypt\").pipe(\n Options.withDescription(\"For org appends: send fields in plaintext even when the org vault is unlocked.\"),\n);\n\nconst instanceOption = Options.text(\"instance\").pipe(\n Options.withDescription(\n \"With a group append token (grptsk_ group): append only to these member task instances (tsk_ ids printed by `sp task`). Repeatable; without it the subtask goes to every member.\",\n ),\n Options.repeated,\n);\n\nexport const subtaskCommand = Command.make(\n \"subtask\",\n {\n \"append-token\": appendTokenOption,\n title: titleOption,\n content: contentOption,\n \"text-input\": textInput,\n \"choice-input\": choiceInput,\n \"action-input\": actionInput,\n \"slider-input\": sliderInput,\n \"photo-input\": photoInput,\n \"voice-recording-input\": voiceRecordingInput,\n \"file-input\": fileInput,\n \"location-input\": locationInput,\n link: linkOption,\n file: fileOption,\n submit: submitOption,\n markdown: markdownOption,\n \"no-encrypt\": noEncryptOption,\n instance: instanceOption,\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const appendToken = args[\"append-token\"];\n const title = Option.getOrUndefined(args.title);\n const content = Option.getOrUndefined(args.content);\n const topic = args.topic[0];\n const inputs = yield* Effect.try({\n try: () => buildInputs(args),\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n\n if (content === undefined && inputs.length === 0) {\n return yield* Effect.fail(new UserError({ message: \"a subtask needs --content or at least one input\" }));\n }\n\n const opts: SendSubtaskOptions = {\n ...(title !== undefined ? { title } : {}),\n ...(content !== undefined ? { content } : {}),\n ...(inputs.length > 0 ? { inputs } : {}),\n links: [...args.link],\n autoCommit: !args.submit,\n ...(args.markdown ? { contentFormat: \"markdown\" as const } : {}),\n };\n const instances = args.instance.length > 0 ? [...args.instance] : undefined;\n\n // Personal append (-k/--topic): encrypt under the topic key (must match\n // the parent's), POST /v1/subtasks/json with the API-Token.\n if (topic !== undefined) {\n // Read each --file off disk into a FileAttachment; the SDK encrypts the\n // bytes (when the topic has a password) and drives the upload lifecycle.\n const files = yield* buildFiles(args.file);\n const apiToken = yield* requireApiToken(args[\"api-token\"]);\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireClient({ baseUrl: args[\"base-url\"], apiToken, passwords: args.password });\n if (willEncrypt(args.password, topic)) {\n yield* out.info(\"encrypting outgoing subtask (Argon2id, this takes a moment)\");\n }\n const resp = yield* sdkCall(\"subtask append\", () =>\n client.appendSubtask({\n appendToken,\n topic,\n ...(instances !== undefined ? { instances } : {}),\n ...opts,\n ...(files.length > 0 ? { files } : {}),\n }),\n );\n yield* printSubtaskResponse(resp);\n }),\n );\n return;\n }\n\n // Org append (default): CLI bearer session + org-vault field encryption,\n // POST /v1/org/subtasks/json. The parent must be a task in your org.\n yield* sendOrgSubtask({ appendToken, opts, instances, files: [...args.file], noEncrypt: args[\"no-encrypt\"] });\n }),\n);\n\n/** Shared output contract for both append paths. Single append: one subtask id\n * on stdout. Group append (grptsk_ token): one subtask id per member on stdout,\n * with the taskId -> subtaskId mapping on the info channel. */\nconst printSubtaskResponse = (resp: CreateSubtaskResponse, suffix = \"\") =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n if (isSubtaskGroupResponse(resp)) {\n yield* out.info(`subtask appended to group ${resp.groupId} (${resp.subtasks.length} member${resp.subtasks.length === 1 ? \"\" : \"s\"})${suffix}`);\n yield* Effect.forEach(resp.subtasks, (s) =>\n out.info(`instance: ${s.taskId} -> subtask ${s.subtaskId}`).pipe(Effect.zipRight(out.print(s.subtaskId))),\n );\n } else {\n yield* out.info(`subtask appended: ${resp.subtaskId}${suffix}`);\n yield* out.print(resp.subtaskId);\n }\n });\n\n/** Append a subtask to an org task over the CLI bearer session, encrypting each\n * field under the current org master_key when the vault is unlocked. Mirrors\n * `sendOrgTask` in task.ts; the subtask inherits the parent's recipients. */\nconst sendOrgSubtask = (params: {\n appendToken: string;\n opts: SendSubtaskOptions;\n instances: string[] | undefined;\n files: string[];\n noEncrypt: boolean;\n}) =>\n Effect.gen(function* () {\n const api = yield* Api;\n const access = yield* VaultAccess;\n\n // Auto-encrypt when the org vault is unlocked (prompting inline on a fresh\n // machine instead of silently going plaintext).\n const vault = yield* access.forSendOrPlaintext(params.noEncrypt);\n\n const masterKey = vault\n ? { key: vault.masterKeyCurrent.key, version: vault.masterKeyCurrent.version }\n : undefined;\n // Files ride the same lifecycle as an Api-Key SDK append: metadata in the\n // append body (bytes encrypted under the org master key when the vault is\n // unlocked), then presign -> PUT -> complete against the org bearer\n // attachment endpoints. A group append mints ONE shared attachment set.\n const fileAttachments = yield* buildFiles(params.files);\n const prepared = yield* sdkCall(\"prepare attachments\", () => prepareFileAttachments(fileAttachments, masterKey?.key));\n const body = yield* sdkCall(\"build subtask request\", () =>\n buildOrgSubtaskRequest(params.appendToken, params.opts, masterKey, params.instances, prepared.map((p) => p.meta)),\n );\n\n // The response union is SDK-owned (single vs group); decode leniently and\n // let the SDK's type guard do the discrimination.\n const payload = (yield* api.postJson(\"subtask append\", \"/v1/org/subtasks/json\", Schema.Unknown, body)) as CreateSubtaskResponse;\n yield* uploadOrgAttachments(prepared, payload.attachments);\n yield* printSubtaskResponse(\n payload,\n vault ? ` (encrypted under master_key v${vault.masterKeyCurrent.version})` : \" (plaintext)\",\n );\n });\n","// Shebang is added by tsdown via its `banner` option; do not duplicate it here, or\n// rolldown emits DUPLICATE_SHEBANG and produces an empty bundle.\n//\n// Composition root: the full Layer graph (platform services + the CLI's own\n// services) is provided ONCE here, and the top-level catchAllCause is the only\n// place errors become stderr text (see errors.ts). runMain's teardown exits\n// the process, which is also what keeps undici's keep-alive sockets from\n// holding the event loop open after `auth login`.\n\nimport { Command } from \"@effect/cli\";\nimport { FetchHttpClient } from \"@effect/platform\";\nimport { NodeContext, NodeRuntime } from \"@effect/platform-node\";\nimport { Cause, Effect, Layer, Option } from \"effect\";\n\nimport { renderError } from \"./errors.js\";\nimport { CliOutput } from \"./services/output.js\";\nimport { Sodium } from \"./crypto/sodium.js\";\nimport { AuthStore, InviteStore, VaultStore } from \"./services/stores.js\";\nimport { Api } from \"./services/api.js\";\nimport { VaultAccess } from \"./services/vault-access.js\";\n\nimport { authCommand } from \"./commands/auth.js\";\nimport { collectCommand } from \"./commands/collect.js\";\nimport { daemonCommand } from \"./commands/daemon.js\";\nimport { downloadCommand } from \"./commands/download.js\";\nimport { eventsCommand } from \"./commands/events.js\";\nimport { notifyCommand } from \"./commands/notify.js\";\nimport { orgCommand } from \"./commands/org.js\";\nimport { taskCommand } from \"./commands/task.js\";\nimport { subtaskCommand } from \"./commands/subtask.js\";\n\n// Streams can also surface a broken pipe as an async 'error' event (bypassing\n// CliOutput's sync try/catch — e.g. @effect/cli's own help output). Same\n// contract as CliOutput: stdout's reader gone → done, exit clean; stderr is\n// best-effort. Anything else stays fatal.\nconst onPipeError = (exit: boolean) => (e: NodeJS.ErrnoException) => {\n if (e.code !== \"EPIPE\") throw e;\n if (exit) process.exit(0);\n};\nprocess.stdout.on(\"error\", onPipeError(true));\nprocess.stderr.on(\"error\", onPipeError(false));\n\nconst root = Command.make(\"simplepush\").pipe(\n Command.withSubcommands([authCommand, orgCommand, eventsCommand, collectCommand, daemonCommand, downloadCommand, notifyCommand, taskCommand, subtaskCommand]),\n);\n\nconst cli = Command.run(root, {\n name: \"Simplepush CLI\",\n version: \"0.1.0\",\n});\n\n// Service layers. Each service's `dependencies` covers its siblings; the\n// platform capabilities (FileSystem, Path, Terminal, HttpClient) come from\n// NodeContext + FetchHttpClient underneath. Layer memoization guarantees one\n// instance of each service per run.\nconst MainLive = Layer.mergeAll(\n CliOutput.Default,\n Sodium.Default,\n AuthStore.Default,\n VaultStore.Default,\n InviteStore.Default,\n Api.Default,\n VaultAccess.Default,\n).pipe(\n Layer.provideMerge(FetchHttpClient.layer),\n Layer.provideMerge(NodeContext.layer),\n);\n\n/** Render any failure through the typed-error table, then re-fail so the\n * runtime exits non-zero. Defects (bugs) keep their full pretty cause. */\nconst reportErrors = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n Effect.catchAllCause((cause) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n if (!Cause.isInterruptedOnly(cause)) {\n const failure = Cause.failureOption(cause);\n if (Option.isSome(failure)) {\n const message = renderError(failure.value);\n if (message !== undefined) yield* out.error(message);\n } else {\n yield* out.error(Cause.pretty(cause));\n }\n }\n return yield* Effect.failCause(cause);\n }),\n ),\n );\n\ncli(process.argv).pipe(\n reportErrors,\n Effect.provide(MainLive),\n NodeRuntime.runMain({ disableErrorReporting: true }),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"main.mjs","names":["nodeRandomBytes","idArg","noEncryptOption","formatOption","contentOption","titleOption","memberOption","broadcastOption","orgTopicOption","tagOption","noEncryptOption","sharedOption","formatOption","WrappedKey","idArg","titleOption","contentOption","textInput","choiceInput","actionInput","sliderInput","photoInput","voiceRecordingInput","fileInput","locationInput","linkOption","fileOption","submitOption","waitOption","replyOption","noEncryptOption","markdownOption","formatOption"],"sources":["../src/errors.ts","../src/services/output.ts","../src/crypto/params.ts","../src/crypto/sodium.ts","../src/services/stores.ts","../src/services/api.ts","../src/services/vault-access.ts","../src/global-options.ts","../src/format.ts","../src/commands/auth.ts","../src/services/sdk.ts","../src/daemon/paths.ts","../src/daemon/server.ts","../src/daemon/transport.ts","../src/since.ts","../src/collect-output.ts","../src/save-files.ts","../src/until.ts","../src/commands/collect.ts","../src/commands/cancel.ts","../src/commands/daemon.ts","../src/commands/download.ts","../src/output.ts","../src/commands/events.ts","../src/input-spec.ts","../src/commands/notify.ts","../src/commands/integration.ts","../src/commands/org-encryption.ts","../src/commands/org.ts","../src/files.ts","../src/commands/task.ts","../src/commands/subtask.ts","../src/main.ts"],"sourcesContent":["// The CLI's typed error taxonomy. Every failure a command can produce is a\n// `Data.TaggedError` carried on the Effect error channel; `renderError` at the\n// top of main.ts is the ONLY place errors become stderr text. A handler that\n// has already told the user what went wrong fails with `Aborted` (rendered as\n// nothing) so the process still exits 1 without a duplicate message.\n\nimport { ValidationError } from \"@effect/cli\";\nimport { Terminal } from \"@effect/platform\";\nimport { Data } from \"effect\";\nimport { ParseResult } from \"effect\";\n\n/** No CLI session saved — `sp auth login` hasn't been run (or was logged out). */\nexport class NotLoggedIn extends Data.TaggedError(\"NotLoggedIn\")<{}> {}\n\n/** A personal-credential command was invoked without --api-token / $SP_API_TOKEN. */\nexport class MissingApiToken extends Data.TaggedError(\"MissingApiToken\")<{}> {}\n\n/** The backend answered non-2xx. `action` names the operation for the message. */\nexport class ApiFailure extends Data.TaggedError(\"ApiFailure\")<{\n readonly action: string;\n readonly status: number;\n readonly detail: string;\n /** Wire error code from the `{error, msg}` body (e.g. \"service_unavailable\",\n * \"idempotency_in_flight\") — what retry policies branch on. */\n readonly code?: string;\n}> {}\n\n/** The request never produced an HTTP response (DNS, refused, TLS...). */\nexport class TransportFailure extends Data.TaggedError(\"TransportFailure\")<{\n readonly action: string;\n readonly cause: unknown;\n}> {}\n\n/** A promise-based SDK call or stream failed. */\nexport class SdkFailure extends Data.TaggedError(\"SdkFailure\")<{\n readonly action: string;\n readonly cause: unknown;\n}> {}\n\n/** A libsodium operation failed or was fed mis-sized material. */\nexport class CryptoFailure extends Data.TaggedError(\"CryptoFailure\")<{\n readonly message: string;\n}> {}\n\n/** Vault blob would not decrypt — wrong passphrase or corrupted blob. */\nexport class VaultUnlockFailed extends Data.TaggedError(\"VaultUnlockFailed\")<{}> {}\n\n/** The org has no encryption config enabled. */\nexport class EncryptionDisabled extends Data.TaggedError(\"EncryptionDisabled\")<{}> {}\n\n/** A user-facing validation / usage error with a ready-to-print message. */\nexport class UserError extends Data.TaggedError(\"UserError\")<{\n readonly message: string;\n}> {}\n\n/** The failure has already been reported on stderr — exit 1 silently. */\nexport class Aborted extends Data.TaggedError(\"Aborted\")<{}> {}\n\nconst causeMessage = (cause: unknown): string =>\n cause instanceof Error ? cause.message : String(cause);\n\n/** One error -> one stderr line (sans the `error: ` prefix added by the\n * renderer). `undefined` means \"print nothing\" — the error was already\n * reported (Aborted), or another layer printed it (@effect/cli usage text,\n * an interrupted prompt). */\nexport function renderError(e: unknown): string | undefined {\n if (e instanceof NotLoggedIn) return \"not logged in. Run `sp auth login` first.\";\n if (e instanceof MissingApiToken) return \"an API token is required: pass --api-token or set $SP_API_TOKEN\";\n if (e instanceof ApiFailure) return `${e.action} failed (${e.status}): ${e.detail}`;\n if (e instanceof TransportFailure) return `${e.action} failed: ${causeMessage(e.cause)}`;\n if (e instanceof SdkFailure) return `${e.action} failed: ${causeMessage(e.cause)}`;\n if (e instanceof CryptoFailure) return e.message;\n if (e instanceof VaultUnlockFailed) return \"could not unlock the vault — passphrase is wrong, or the vault is corrupted.\";\n if (e instanceof EncryptionDisabled) return \"encryption is not enabled for this org. Run `org encryption enable` first.\";\n if (e instanceof UserError) return e.message;\n if (e instanceof Aborted) return undefined;\n // @effect/cli already printed the usage text for a bad invocation.\n if (ValidationError.isValidationError(e)) return undefined;\n // Ctrl-C at an interactive prompt — nothing to add.\n if (e instanceof Terminal.QuitException) return undefined;\n if (ParseResult.isParseError(e)) return ParseResult.TreeFormatter.formatErrorSync(e);\n return causeMessage(e);\n}\n","// All terminal output funnels through this service. stderr carries the\n// info/warn/error narration (info gated by --quiet); stdout carries ONLY\n// payload lines (ids, NDJSON envelopes) — that split is the scripting contract.\n//\n// `quiet` lives in a Ref set by each command handler after parsing its flags.\n\nimport { Effect, Ref } from \"effect\";\n\nconst isEpipe = (e: unknown): boolean =>\n e instanceof Error && (e as NodeJS.ErrnoException).code === \"EPIPE\";\n\nexport class CliOutput extends Effect.Service<CliOutput>()(\"cli/CliOutput\", {\n effect: Effect.gen(function* () {\n const quietRef = yield* Ref.make(false);\n\n // Narration is best-effort: a closed stderr must not kill the command.\n const stderr = (line: string) =>\n Effect.sync(() => {\n try {\n process.stderr.write(line + \"\\n\");\n } catch (e) {\n if (!isEpipe(e)) throw e;\n }\n });\n\n return {\n setQuiet: (quiet: boolean) => Ref.set(quietRef, quiet),\n info: (msg: string) =>\n Effect.flatMap(Ref.get(quietRef), (quiet) => (quiet ? Effect.void : stderr(`info: ${msg}`))),\n warn: (msg: string) => stderr(`warn: ${msg}`),\n error: (msg: string) => stderr(`error: ${msg}`),\n /** One payload line on stdout, flushed immediately. EPIPE means the\n * downstream reader is gone (`sp collect | head`, a dead pipeline\n * neighbor): stop writing and exit quietly, like any pipeline citizen. */\n print: (line: string) =>\n Effect.sync(() => {\n try {\n process.stdout.write(line + \"\\n\");\n } catch (e) {\n if (isEpipe(e)) process.exit(0);\n throw e;\n }\n }),\n } as const;\n }),\n}) {}\n","// Pure crypto domain values: KDF parameters, vault content types, and the\n// normalization rules both sides of an invite / passphrase must agree on.\n// Everything touching libsodium lives in the `Sodium` service (./sodium.ts).\n\nimport { createHash } from \"node:crypto\";\nimport { Schema } from \"effect\";\n\n// Argon2id parameters. Stored alongside the vault so we can re-tune them\n// without invalidating existing vaults — every unlock reads the params back\n// out of the server-stored kdf_params blob and feeds them in here.\nexport const KdfParams = Schema.Struct({\n algo: Schema.Literal(\"argon2id\"),\n // Iterations (libsodium opslimit).\n t: Schema.Number,\n // Memory in bytes (libsodium memlimit). 64 MiB by default.\n m: Schema.Number,\n // Parallelism (lane count). libsodium's high-level wrapper fixes this at 1\n // and ignores any other value; kept for forward compat and for the\n // server-side params record.\n p: Schema.Number,\n});\nexport type KdfParams = typeof KdfParams.Type;\n\nexport const DEFAULT_KDF_PARAMS: KdfParams = {\n algo: \"argon2id\",\n t: 3,\n m: 64 * 1024 * 1024,\n p: 1,\n};\n\n// 32 bytes — long enough to key the XChaCha20-Poly1305 AEAD used for the\n// vault blob.\nexport const VAULT_KEY_BYTES = 32;\n\n// The BIP39 English wordlist has exactly 2048 entries → 11 bits per word.\n// 8 words ≈ 88 bits, the design target.\nexport const DEFAULT_WORD_COUNT = 8;\n\nconst MasterKeySchema = Schema.Struct({\n version: Schema.Number,\n key: Schema.Uint8ArrayFromSelf,\n});\nexport type MasterKey = typeof MasterKeySchema.Type;\n\n// Plaintext contents of the org encryption vault. Encrypted under `vault_key`\n// (derived from the org passphrase) and stored server-side as a single blob.\n//\n// `adminPrivateKey` plus `masterKeyCurrent` are what any admin's CLI needs\n// to operate; `masterKeyHistory` lets admins decrypt notifications encrypted\n// under prior versions and re-wrap them to new devices on demand.\n// Pin of one org integration's public key, recorded at `sp integration\n// create`. Load-bearing at ROTATION: the CLI wraps a freshly rotated\n// master_key to whatever pubkey the backend hands back for an integration,\n// and without this pin a malicious backend could substitute a key it controls\n// and receive the new master key in the clear. Substitution at create time is\n// harmless (the blobs were already sealed to the real key) — rotation is the\n// window this closes. The vault is the right home: it is opaque to the\n// backend and survives admin machine moves.\nexport const IntegrationPin = Schema.Struct({\n id: Schema.String,\n pubkeyB64: Schema.String,\n name: Schema.String,\n});\nexport type IntegrationPin = typeof IntegrationPin.Type;\n\nexport const VaultContents = Schema.Struct({\n adminPublicKey: Schema.Uint8ArrayFromSelf,\n adminPrivateKey: Schema.Uint8ArrayFromSelf,\n masterKeyCurrent: MasterKeySchema,\n masterKeyHistory: Schema.Array(MasterKeySchema),\n // optionalWith default keeps vault blobs written before this field existed\n // decoding cleanly — an absent list is an empty list.\n integrations: Schema.optionalWith(Schema.Array(IntegrationPin), { default: () => [] }),\n});\nexport type VaultContents = typeof VaultContents.Type;\n\n// Serialized vault shape (inside the encrypted blob): bytes base64-encoded\n// plus a format version so the schema can evolve without ambiguity.\nconst MasterKeyJson = Schema.Struct({\n version: Schema.Number,\n key: Schema.Uint8ArrayFromBase64,\n});\n\nexport const VaultJson = Schema.Struct({\n formatVersion: Schema.Literal(1),\n adminPublicKey: Schema.Uint8ArrayFromBase64,\n adminPrivateKey: Schema.Uint8ArrayFromBase64,\n masterKeyCurrent: MasterKeyJson,\n masterKeyHistory: Schema.Array(MasterKeyJson),\n integrations: Schema.optionalWith(Schema.Array(IntegrationPin), { default: () => [] }),\n});\n\n// Light normalization for human-typed input: trim, collapse whitespace,\n// lowercase. Lets the user paste \" Foo bar\\nbaz \" and have it match\n// \"foo bar baz\". Does not change semantics if input is already canonical.\nexport function normalizePassphrase(input: string): string {\n return input.trim().toLowerCase().split(/\\s+/).join(\" \");\n}\n\n// Normalization mirrors backend/util/InviteCode.normalize:\n// trim → strip dashes and whitespace → uppercase.\n// So \"abcd-efgh\", \"ABCDEFGH\", and \"abcd efgh\" all hash identically. Both\n// sides MUST agree on this rule or HMAC verification silently fails.\nexport function normalizeInviteCode(input: string): string {\n return input.trim().toUpperCase().replace(/[-\\s]/g, \"\");\n}\n\n// Hash must match the backend's `InviteCode.hash`:\n// sha256(normalize(code)).hex()\n// The backend only ever sees this hash — the admin's CLI is the sole holder\n// of the cleartext, so a compromised backend can't later forge an HMAC for a\n// substituted device pubkey at sync time.\nexport function hashInviteCode(plain: string): string {\n return createHash(\"sha256\").update(normalizeInviteCode(plain), \"utf8\").digest(\"hex\");\n}\n","// The `Sodium` service owns libsodium-wrappers-sumo (async wasm init happens\n// once, in the layer, instead of a top-level await every importer pays for)\n// and exposes the CLI's crypto operations as Effects failing with a typed\n// `CryptoFailure`. The sumo variant is required for crypto_pwhash (Argon2id),\n// which the standard build omits.\n\nimport { randomBytes as nodeRandomBytes } from \"node:crypto\";\nimport _sodium from \"libsodium-wrappers-sumo\";\nimport { wordlist } from \"@scure/bip39/wordlists/english.js\";\nimport { Effect, Schema } from \"effect\";\n\nimport { CryptoFailure } from \"../errors.js\";\nimport {\n DEFAULT_KDF_PARAMS,\n DEFAULT_WORD_COUNT,\n VAULT_KEY_BYTES,\n VaultJson,\n normalizeInviteCode,\n type KdfParams,\n type VaultContents,\n} from \"./params.js\";\n\nexport interface AdminKeyPair {\n readonly publicKey: Uint8Array;\n readonly privateKey: Uint8Array;\n}\n\nconst WRAP_NONCE_BYTES = 24;\nconst VAULT_NONCE_BYTES = 24;\n\n// Crockford-base32-ish invite alphabet: 32 chars, no 0/1/I/L/O.\n// 256 % 32 = 0 → unbiased modulo.\nconst InviteAlphabet = \"ABCDEFGHJKMNPQRSTUVWXYZ23456789\";\nconst InviteGroupSize = 4;\nconst InviteGroups = 2;\n\nconst encodeVaultJson = Schema.encodeSync(VaultJson);\nconst decodeVaultJson = Schema.decodeUnknownSync(Schema.parseJson(VaultJson));\n\nexport class Sodium extends Effect.Service<Sodium>()(\"cli/Sodium\", {\n effect: Effect.gen(function* () {\n const sodium = yield* Effect.promise(() => _sodium.ready.then(() => _sodium));\n\n const fail = (message: string) => new CryptoFailure({ message });\n const attempt = <A>(message: string, f: () => A): Effect.Effect<A, CryptoFailure> =>\n Effect.try({ try: f, catch: (e) => fail(e instanceof CryptoFailure ? e.message : message) });\n\n // Plain (non-Effect) codecs — deterministic, infallible on our inputs.\n const toB64 = (bytes: Uint8Array): string => sodium.to_base64(bytes, sodium.base64_variants.ORIGINAL);\n const fromB64 = (s: string): Uint8Array => sodium.from_base64(s, sodium.base64_variants.ORIGINAL);\n // URL-safe, unpadded — for values that live inside composite tokens or\n // config files ('+' and '/' invite quoting accidents; '=' padding invites\n // truncation). No base64 variant emits '.', so a dot stays a safe\n // separator either way.\n const toB64Url = (bytes: Uint8Array): string => sodium.to_base64(bytes, sodium.base64_variants.URLSAFE_NO_PADDING);\n\n return {\n toB64,\n fromB64,\n toB64Url,\n\n randomBytes: (length: number) => Effect.sync(() => sodium.randombytes_buf(length)),\n // X25519 keypair from a 32-byte seed — the integration-token shape: the\n // seed IS the token's right half, so deriving (not storing) the keypair\n // means the token alone reconstructs everything client-side.\n seedKeypair: (seed: Uint8Array) =>\n attempt(\"keypair derivation failed\", () => sodium.crypto_box_seed_keypair(seed)),\n\n // 32-byte salt for vault-key derivation. The design stores it server-side\n // so it can be rotated independently of the passphrase.\n generateVaultSalt: Effect.sync(() => sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES)),\n\n // Symmetric key for AEAD: 32 bytes, used as `master_key_vN`.\n generateMasterKey: Effect.sync(() =>\n sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES),\n ),\n\n // X25519 keypair for `crypto_box` wraps of master_key to device pubkeys.\n generateAdminKeyPair: Effect.sync((): AdminKeyPair => {\n const kp = sodium.crypto_box_keypair();\n return { publicKey: kp.publicKey, privateKey: kp.privateKey };\n }),\n\n // We deliberately do NOT use BIP39 mnemonic encoding (which folds in a\n // checksum and only allows specific word counts) — this is a passphrase,\n // not a wallet seed.\n generatePassphrase: (wordCount: number = DEFAULT_WORD_COUNT) =>\n Effect.gen(function* () {\n if (!Number.isInteger(wordCount) || wordCount < 1) {\n return yield* fail(`wordCount must be a positive integer (got ${wordCount})`);\n }\n if (wordlist.length !== 2048) {\n return yield* fail(`unexpected wordlist length: ${wordlist.length}`);\n }\n const words: string[] = [];\n for (let i = 0; i < wordCount; i++) {\n // randombytes_uniform does unbiased rejection sampling under the hood.\n words.push(wordlist[sodium.randombytes_uniform(wordlist.length)]!);\n }\n return words.join(\" \");\n }),\n\n // Cleartext invite code (uses node:crypto randomness, grouped for humans).\n generateInviteCode: Effect.sync(() => {\n const bytes = nodeRandomBytes(InviteGroupSize * InviteGroups);\n const groups: string[] = [];\n for (let g = 0; g < InviteGroups; g++) {\n let group = \"\";\n for (let i = 0; i < InviteGroupSize; i++) {\n group += InviteAlphabet.charAt(bytes[g * InviteGroupSize + i]! % InviteAlphabet.length);\n }\n groups.push(group);\n }\n return groups.join(\"-\");\n }),\n\n deriveVaultKey: (passphrase: string, salt: Uint8Array, params: KdfParams = DEFAULT_KDF_PARAMS) =>\n Effect.gen(function* () {\n if ((params.algo as string) !== \"argon2id\") {\n return yield* fail(`unsupported KDF algorithm: ${params.algo}`);\n }\n if (salt.length !== sodium.crypto_pwhash_SALTBYTES) {\n return yield* fail(`salt must be ${sodium.crypto_pwhash_SALTBYTES} bytes (got ${salt.length})`);\n }\n return yield* attempt(\"key derivation failed\", () =>\n sodium.crypto_pwhash(VAULT_KEY_BYTES, passphrase, salt, params.t, params.m, sodium.crypto_pwhash_ALG_ARGON2ID13),\n );\n }),\n\n encryptVault: (contents: VaultContents, vaultKey: Uint8Array) =>\n Effect.gen(function* () {\n if (vaultKey.length !== sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) {\n return yield* fail(`vaultKey must be ${sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES} bytes`);\n }\n return yield* attempt(\"vault encryption failed\", () => {\n const json = encodeVaultJson({ formatVersion: 1, ...contents });\n const plaintext = new TextEncoder().encode(JSON.stringify(json));\n const nonce = sodium.randombytes_buf(VAULT_NONCE_BYTES);\n const ct = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, null, null, nonce, vaultKey);\n // Concatenate nonce || ciphertext so callers only persist one blob.\n const out = new Uint8Array(nonce.length + ct.length);\n out.set(nonce, 0);\n out.set(ct, nonce.length);\n return out;\n });\n }),\n\n decryptVault: (blob: Uint8Array, vaultKey: Uint8Array) =>\n Effect.gen(function* () {\n if (vaultKey.length !== sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) {\n return yield* fail(`vaultKey must be ${sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES} bytes`);\n }\n if (blob.length < VAULT_NONCE_BYTES + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) {\n return yield* fail(\"vault blob is truncated\");\n }\n return yield* attempt(\"vault decryption failed\", (): VaultContents => {\n const nonce = blob.slice(0, VAULT_NONCE_BYTES);\n const ct = blob.slice(VAULT_NONCE_BYTES);\n // crypto_aead_*_decrypt throws on auth-tag mismatch (wrong key, tampered blob).\n const plaintext = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, ct, null, nonce, vaultKey);\n return decodeVaultJson(new TextDecoder().decode(plaintext));\n });\n }),\n\n // Wraps a master key from the admin to a single device.\n //\n // `crypto_box_easy` (X25519 + XSalsa20 + Poly1305) authenticates the\n // sender, so the device knows the wrap came from someone holding\n // `adminPrivateKey` — the malicious-backend \"swap a wrap\" attack is\n // rejected at the unwrap step because the auth tag won't verify against\n // the pinned admin pubkey. We deliberately do NOT use `crypto_box_seal`\n // (anonymous), which would let the backend forge wraps to keys it controls.\n wrapMasterKey: (masterKey: Uint8Array, adminPrivateKey: Uint8Array, devicePublicKey: Uint8Array) =>\n Effect.gen(function* () {\n if (adminPrivateKey.length !== sodium.crypto_box_SECRETKEYBYTES) {\n return yield* fail(`adminPrivateKey must be ${sodium.crypto_box_SECRETKEYBYTES} bytes`);\n }\n if (devicePublicKey.length !== sodium.crypto_box_PUBLICKEYBYTES) {\n return yield* fail(`devicePublicKey must be ${sodium.crypto_box_PUBLICKEYBYTES} bytes`);\n }\n return yield* attempt(\"master-key wrap failed\", () => {\n const nonce = sodium.randombytes_buf(WRAP_NONCE_BYTES);\n const ct = sodium.crypto_box_easy(masterKey, nonce, devicePublicKey, adminPrivateKey);\n const out = new Uint8Array(nonce.length + ct.length);\n out.set(nonce, 0);\n out.set(ct, nonce.length);\n return out;\n });\n }),\n\n unwrapMasterKey: (blob: Uint8Array, devicePrivateKey: Uint8Array, adminPublicKey: Uint8Array) =>\n Effect.gen(function* () {\n if (devicePrivateKey.length !== sodium.crypto_box_SECRETKEYBYTES) {\n return yield* fail(`devicePrivateKey must be ${sodium.crypto_box_SECRETKEYBYTES} bytes`);\n }\n if (adminPublicKey.length !== sodium.crypto_box_PUBLICKEYBYTES) {\n return yield* fail(`adminPublicKey must be ${sodium.crypto_box_PUBLICKEYBYTES} bytes`);\n }\n if (blob.length < WRAP_NONCE_BYTES + sodium.crypto_box_MACBYTES) {\n return yield* fail(\"wrapped blob is truncated\");\n }\n return yield* attempt(\"master-key unwrap failed\", () =>\n sodium.crypto_box_open_easy(blob.slice(WRAP_NONCE_BYTES), blob.slice(0, WRAP_NONCE_BYTES), adminPublicKey, devicePrivateKey),\n );\n }),\n\n // HMAC binding between an invite code and a device pubkey, used to confirm\n // that the pubkey was submitted by whoever actually held the invite.\n // Normalizes the code first (both sides MUST agree on that rule).\n // Single-call `crypto_auth_hmacsha256(message, key)` requires a fixed-length\n // (32-byte) key; the streaming API accepts variable-length keys (proper\n // RFC 2104 HMAC behavior), which is what an 8-char invite code needs.\n hmacInviteBinding: (inviteCode: string, devicePublicKey: Uint8Array): Uint8Array => {\n const state = sodium.crypto_auth_hmacsha256_init(normalizeInviteCode(inviteCode));\n sodium.crypto_auth_hmacsha256_update(state, devicePublicKey);\n return sodium.crypto_auth_hmacsha256_final(state);\n },\n\n // Constant-time equality for HMAC verification. Critical for not leaking\n // timing information when the CLI brute-forces its invite-code list\n // against each device row.\n constantTimeEqual: (a: Uint8Array, b: Uint8Array): boolean =>\n a.length === b.length && sodium.memcmp(a, b),\n } as const;\n }),\n}) {}\n","// Local persistence under ~/.config/simplepush (or %APPDATA%\\simplepush on\n// Windows): auth.json (CLI session), vault.json (unlocked org-vault cache),\n// invites.json (issued invite cleartexts). All three are Schema-validated\n// JSON files with mode 0600 in a 0700 dir — same trust class.\n\nimport { homedir } from \"node:os\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport type { PlatformError } from \"@effect/platform/Error\";\nimport { Effect, Option, ParseResult, Redacted, Schema } from \"effect\";\n\nimport { IntegrationPin, VaultContents } from \"../crypto/index.js\";\n\nexport function configDir(): string {\n if (process.platform === \"win32\") {\n const appData = process.env.APPDATA ?? `${homedir()}/AppData/Roaming`;\n return `${appData}/simplepush`;\n }\n const xdg = process.env.XDG_CONFIG_HOME;\n return xdg ? `${xdg}/simplepush` : `${homedir()}/.config/simplepush`;\n}\n\nconst isNotFound = (e: PlatformError | ParseResult.ParseError): boolean =>\n e._tag === \"SystemError\" && e.reason === \"NotFound\";\n\n/** One Schema-validated JSON file with 0600 perms. `load` distinguishes\n * \"absent\" (Option.none) from real IO/decode failures. */\nconst jsonFile = <A, I>(fileName: string, schema: Schema.Schema<A, I>) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n const decode = Schema.decodeUnknown(Schema.parseJson(schema));\n const encode = Schema.encode(schema);\n const filePath = path.join(configDir(), fileName);\n\n const load: Effect.Effect<Option.Option<A>, PlatformError | ParseResult.ParseError> = fs\n .readFileString(filePath)\n .pipe(\n Effect.flatMap(decode),\n Effect.map(Option.some),\n Effect.catchIf(isNotFound, () => Effect.succeed(Option.none<A>())),\n );\n\n const save = (value: A): Effect.Effect<string, PlatformError | ParseResult.ParseError> =>\n Effect.gen(function* () {\n yield* fs.makeDirectory(configDir(), { recursive: true }).pipe(Effect.ignore);\n yield* fs.chmod(configDir(), 0o700).pipe(Effect.ignore);\n const encoded = yield* encode(value);\n yield* fs.writeFileString(filePath, JSON.stringify(encoded, null, 2));\n // writeFile only enforces mode on creation; chmod again so re-saving an\n // existing (looser-permissioned) file tightens it.\n if (process.platform !== \"win32\") yield* fs.chmod(filePath, 0o600);\n return filePath;\n });\n\n /** Removes the file; false when there was nothing to remove. */\n const clear: Effect.Effect<boolean, PlatformError> = fs.remove(filePath).pipe(\n Effect.as(true),\n Effect.catchIf(isNotFound, () => Effect.succeed(false)),\n );\n\n return { filePath, load, save, clear } as const;\n });\n\n// ---------- auth ----------\n\nexport const StoredAuth = Schema.Struct({\n baseUrl: Schema.String,\n // Redacted so the session token can't leak through logs / error output.\n token: Schema.Redacted(Schema.String),\n loggedInAt: Schema.String,\n});\nexport type StoredAuth = typeof StoredAuth.Type;\n\nexport const bearerToken = (auth: StoredAuth): string => Redacted.value(auth.token);\n\nexport class AuthStore extends Effect.Service<AuthStore>()(\"cli/AuthStore\", {\n effect: jsonFile(\"auth.json\", StoredAuth),\n}) {}\n\n// ---------- org vault cache ----------\n\n// On-disk vault.json shape: base64-encoded key material plus a format version.\n// Stable — existing files must keep decoding.\nconst StoredVault = Schema.Struct({\n formatVersion: Schema.Literal(1),\n adminPublicKeyB64: Schema.Uint8ArrayFromBase64,\n adminPrivateKeyB64: Schema.Uint8ArrayFromBase64,\n masterKeyCurrent: Schema.Struct({ version: Schema.Number, keyB64: Schema.Uint8ArrayFromBase64 }),\n masterKeyHistory: Schema.Array(Schema.Struct({ version: Schema.Number, keyB64: Schema.Uint8ArrayFromBase64 })),\n // Absent in files written before integrations existed — defaulted, so those\n // keep decoding.\n integrations: Schema.optionalWith(Schema.Array(IntegrationPin), { default: () => [] }),\n});\n\nconst VaultFromStored = Schema.transform(StoredVault, VaultContents, {\n strict: true,\n decode: (s): VaultContents => ({\n adminPublicKey: s.adminPublicKeyB64,\n adminPrivateKey: s.adminPrivateKeyB64,\n masterKeyCurrent: { version: s.masterKeyCurrent.version, key: s.masterKeyCurrent.keyB64 },\n masterKeyHistory: s.masterKeyHistory.map((m) => ({ version: m.version, key: m.keyB64 })),\n integrations: s.integrations,\n }),\n encode: (v) => ({\n formatVersion: 1 as const,\n adminPublicKeyB64: v.adminPublicKey,\n adminPrivateKeyB64: v.adminPrivateKey,\n masterKeyCurrent: { version: v.masterKeyCurrent.version, keyB64: v.masterKeyCurrent.key },\n masterKeyHistory: v.masterKeyHistory.map((m) => ({ version: m.version, keyB64: m.key })),\n integrations: v.integrations ?? [],\n }),\n});\n\nexport class VaultStore extends Effect.Service<VaultStore>()(\"cli/VaultStore\", {\n effect: jsonFile(\"vault.json\", VaultFromStored),\n}) {}\n\n// ---------- issued invites ----------\n\n// The CLI keeps issued invite cleartexts so a later `org encryption sync` can\n// verify `HMAC(invite_code, device_pubkey)` against each device row — the\n// backend only ever holds hash(code), deliberately.\nexport const IssuedInvite = Schema.Struct({\n code: Schema.String, // plaintext, exactly as shown to the admin\n name: Schema.String,\n role: Schema.Literal(\"member\", \"admin\"),\n issuedAt: Schema.String, // ISO\n expiresAt: Schema.String, // ISO\n});\nexport type IssuedInvite = typeof IssuedInvite.Type;\n\nconst InvitesFile = Schema.Struct({ invites: Schema.Array(IssuedInvite) });\n\nexport class InviteStore extends Effect.Service<InviteStore>()(\"cli/InviteStore\", {\n effect: Effect.gen(function* () {\n const file = yield* jsonFile(\"invites.json\", InvitesFile);\n\n const loadAll = file.load.pipe(\n Effect.map(Option.match({ onNone: () => [] as ReadonlyArray<IssuedInvite>, onSome: (f) => f.invites })),\n // A corrupt file behaves like an empty one.\n Effect.catchTag(\"ParseError\", () => Effect.succeed([] as ReadonlyArray<IssuedInvite>)),\n );\n\n return {\n filePath: file.filePath,\n clear: file.clear,\n\n /** Drops any prior entry for the same plaintext code so re-issuance under\n * the same code (shouldn't happen, but defensively) can't duplicate. */\n append: (invite: IssuedInvite) =>\n Effect.gen(function* () {\n const existing = yield* loadAll;\n yield* file.save({ invites: [...existing.filter((i) => i.code !== invite.code), invite] });\n }),\n\n /** Currently-valid invites only — past-expiration entries are pruned from\n * disk as a side effect, shrinking the HMAC candidate set per device. */\n listValid: Effect.gen(function* () {\n const now = new Date();\n const all = yield* loadAll;\n const fresh = all.filter((i) => new Date(i.expiresAt) > now);\n if (fresh.length !== all.length) yield* file.save({ invites: fresh });\n return fresh;\n }),\n\n /** Removes the given code (post-sync consumption). Idempotent. */\n consume: (code: string) =>\n Effect.gen(function* () {\n const all = yield* loadAll;\n const next = all.filter((i) => i.code !== code);\n if (next.length === all.length) return false;\n yield* file.save({ invites: next });\n return true;\n }),\n } as const;\n }),\n}) {}\n","// Backend HTTP for bearer-session commands (`sp org …`, org sends). Wraps the\n// platform HttpClient with: base URL + Authorization from the saved CLI\n// session (AuthStore), the standard `{error, msg}` failure-body parse into a\n// typed ApiFailure, and Schema-decoded success bodies.\n\nimport { HttpBody, HttpClient, HttpClientRequest, HttpClientResponse } from \"@effect/platform\";\nimport { Effect, Option, Schedule, Schema } from \"effect\";\n\nimport { ApiFailure, NotLoggedIn, TransportFailure } from \"../errors.js\";\nimport { AuthStore, bearerToken, type StoredAuth } from \"./stores.js\";\n\nconst ErrorBody = Schema.Struct({ error: Schema.String, msg: Schema.String });\nconst decodeErrorBody = Schema.decodeUnknownOption(Schema.parseJson(ErrorBody));\n\nconst trimSlash = (url: string): string => url.replace(/\\/+$/, \"\");\n\nconst isRetryableCreateFailure = (e: unknown): boolean =>\n e instanceof TransportFailure ||\n (e instanceof ApiFailure && (e.status === 503 || (e.status === 409 && e.code === \"idempotency_in_flight\")));\n\n// 1s → 2s → 4s → 8s → 16s between attempts, mirroring the SDK's defaultRetryPolicy.\nconst createRetrySchedule = Schedule.exponential(\"1 seconds\").pipe(\n Schedule.intersect(Schedule.recurs(5)),\n Schedule.whileInput(isRetryableCreateFailure),\n);\n\nexport class Api extends Effect.Service<Api>()(\"cli/Api\", {\n dependencies: [AuthStore.Default],\n effect: Effect.gen(function* () {\n const http = yield* HttpClient.HttpClient;\n const store = yield* AuthStore;\n\n /** The saved CLI session, or NotLoggedIn. */\n const session: Effect.Effect<StoredAuth, NotLoggedIn> = store.load.pipe(\n Effect.orElseSucceed(() => Option.none<StoredAuth>()),\n Effect.flatMap(\n Option.match({\n onNone: () => Effect.fail(new NotLoggedIn()),\n onSome: Effect.succeed,\n }),\n ),\n );\n\n /** Extract the human-readable message from a failed response: the standard\n * `{error, msg}` body when present, the raw body text otherwise. */\n const failWith = (action: string, res: HttpClientResponse.HttpClientResponse) =>\n res.text.pipe(\n Effect.orElseSucceed(() => \"\"),\n Effect.flatMap((body) => {\n const parsed = decodeErrorBody(body);\n const detail = Option.isSome(parsed) && parsed.value.msg ? parsed.value.msg : body || `HTTP ${res.status}`;\n const code = Option.isSome(parsed) ? parsed.value.error : undefined;\n return Effect.fail(new ApiFailure({ action, status: res.status, detail, code }));\n }),\n );\n\n /** Authenticated request; resolves with the (scoped) response once the\n * status is 2xx, fails with ApiFailure/TransportFailure otherwise. */\n const request = (action: string, method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\", pathname: string, body?: unknown) =>\n Effect.gen(function* () {\n const auth = yield* session;\n const base = HttpClientRequest.make(method)(`${trimSlash(auth.baseUrl)}${pathname}`).pipe(\n HttpClientRequest.bearerToken(bearerToken(auth)),\n );\n const req =\n body === undefined\n ? base\n : HttpClientRequest.setBody(base, HttpBody.unsafeJson(body));\n const res = yield* http.execute(req).pipe(\n Effect.mapError((cause) => new TransportFailure({ action, cause })),\n );\n if (res.status >= 400) return yield* failWith(action, res);\n return res;\n });\n\n /** Request + Schema-decode the JSON success body. */\n const requestJson = <A, I>(\n action: string,\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\",\n pathname: string,\n schema: Schema.Schema<A, I>,\n body?: unknown,\n ) =>\n Effect.scoped(\n request(action, method, pathname, body).pipe(\n Effect.flatMap(HttpClientResponse.schemaBodyJson(schema)),\n ),\n );\n\n return {\n session,\n getJson: <A, I>(action: string, pathname: string, schema: Schema.Schema<A, I>) =>\n requestJson(action, \"GET\", pathname, schema),\n postJson: <A, I>(action: string, pathname: string, schema: Schema.Schema<A, I>, body?: unknown) =>\n requestJson(action, \"POST\", pathname, schema, body),\n /** POST for the org create endpoints, whose bodies carry an idempotency\n * key (minted by the SDK's buildOrg*Request): transient failures —\n * transport errors, 503 database-unavailable, 409 idempotency_in_flight —\n * are retried with 1s→16s backoff (~31s total, outlasting a managed-\n * Postgres failover); the backend replays a create that already\n * committed, so a resend never double-sends. */\n postJsonIdempotent: <A, I>(action: string, pathname: string, schema: Schema.Schema<A, I>, body?: unknown) =>\n requestJson(action, \"POST\", pathname, schema, body).pipe(Effect.retry(createRetrySchedule)),\n putJson: <A, I>(action: string, pathname: string, schema: Schema.Schema<A, I>, body?: unknown) =>\n requestJson(action, \"PUT\", pathname, schema, body),\n /** Fire-and-forget variants for endpoints whose response body we ignore. */\n post: (action: string, pathname: string, body?: unknown) =>\n Effect.scoped(Effect.asVoid(request(action, \"POST\", pathname, body))),\n put: (action: string, pathname: string, body?: unknown) =>\n Effect.scoped(Effect.asVoid(request(action, \"PUT\", pathname, body))),\n delete: (action: string, pathname: string) =>\n Effect.scoped(Effect.asVoid(request(action, \"DELETE\", pathname))),\n\n /** Unauthenticated POST against an explicit base URL (the `sp auth login`\n * flows run before any session exists). Returns status + body text so\n * callers can branch on OAuth-style error payloads. */\n unauthedPost: (action: string, url: string, body?: unknown) =>\n Effect.scoped(\n Effect.gen(function* () {\n const base = HttpClientRequest.post(url);\n const req = body === undefined ? base : HttpClientRequest.setBody(base, HttpBody.unsafeJson(body));\n const res = yield* http.execute(req).pipe(\n Effect.mapError((cause) => new TransportFailure({ action, cause })),\n );\n const text = yield* res.text.pipe(Effect.orElseSucceed(() => \"\"));\n return { status: res.status, body: text };\n }),\n ),\n } as const;\n }),\n}) {}\n","// Single entry point for reading the decrypted org vault. Prompts for the\n// passphrase on demand when the local plaintext cache is missing, so callers\n// (`sync`, `notify`, rotations) don't have to coordinate an explicit `unlock`\n// step: ask once when needed, keep around until explicitly cleared.\n\nimport { Prompt } from \"@effect/cli\";\nimport { Effect, Redacted, Schema } from \"effect\";\n\nimport { EncryptionDisabled, VaultUnlockFailed } from \"../errors.js\";\nimport { KdfParams, normalizePassphrase, type VaultContents } from \"../crypto/index.js\";\nimport { Sodium } from \"../crypto/sodium.js\";\nimport { Api } from \"./api.js\";\nimport { CliOutput } from \"./output.js\";\nimport { VaultStore } from \"./stores.js\";\n\n// The four config fields are ABSENT (not null) whenever enabled=false —\n// zio-json omits None on encode. Gate on `enabled`, not on key presence.\nexport const OrgEncryptionConfig = Schema.Struct({\n enabled: Schema.Boolean,\n adminPubkeyB64: Schema.optional(Schema.NullOr(Schema.String)),\n vaultBlobB64: Schema.optional(Schema.NullOr(Schema.String)),\n vaultSaltB64: Schema.optional(Schema.NullOr(Schema.String)),\n kdfParams: Schema.optional(Schema.NullOr(KdfParams)),\n});\nexport type OrgEncryptionConfig = typeof OrgEncryptionConfig.Type;\n\n/** An enabled config, with the unlock material guaranteed present. */\nexport interface EnabledEncryptionConfig {\n readonly vaultBlobB64: string;\n readonly vaultSaltB64: string;\n readonly kdfParams: KdfParams;\n}\n\nasync function readWholeStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks).toString(\"utf8\").replace(/\\r?\\n$/, \"\");\n}\n\nexport class VaultAccess extends Effect.Service<VaultAccess>()(\"cli/VaultAccess\", {\n dependencies: [Api.Default, VaultStore.Default, Sodium.Default, CliOutput.Default],\n effect: Effect.gen(function* () {\n const api = yield* Api;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n const out = yield* CliOutput;\n\n /** Hidden-input passphrase prompt on a TTY; piped stdin passes through\n * unchanged (trailing newline trimmed) for scripted flows. */\n const readPassphrase = (message: string) =>\n process.stdin.isTTY\n ? Prompt.run(Prompt.password({ message })).pipe(Effect.map(Redacted.value))\n : Effect.promise(readWholeStdin);\n\n const fetchConfig = api.getJson(\"fetch encryption config\", \"/v1/org/encryption\", OrgEncryptionConfig);\n\n /** Narrows a config to its enabled shape or fails EncryptionDisabled. */\n const requireEnabled = (cfg: OrgEncryptionConfig) =>\n !cfg.enabled || !cfg.vaultBlobB64 || !cfg.vaultSaltB64 || !cfg.kdfParams\n ? Effect.fail(new EncryptionDisabled())\n : Effect.succeed<EnabledEncryptionConfig>({\n vaultBlobB64: cfg.vaultBlobB64,\n vaultSaltB64: cfg.vaultSaltB64,\n kdfParams: cfg.kdfParams,\n });\n\n /** Prompt → Argon2id → AEAD-open. Any decrypt failure collapses to\n * VaultUnlockFailed (wrong passphrase and corrupt blob are\n * indistinguishable by design of the AEAD). */\n const unlock = (cfg: EnabledEncryptionConfig, promptText: string) =>\n Effect.gen(function* () {\n const passphrase = yield* readPassphrase(promptText);\n const derived = yield* sodium\n .deriveVaultKey(normalizePassphrase(passphrase), sodium.fromB64(cfg.vaultSaltB64), cfg.kdfParams)\n .pipe(Effect.mapError(() => new VaultUnlockFailed()));\n const vault = yield* sodium\n .decryptVault(sodium.fromB64(cfg.vaultBlobB64), derived)\n .pipe(Effect.mapError(() => new VaultUnlockFailed()));\n return { vault, vaultKey: derived };\n });\n\n /** True when the cached vault belongs to THIS org's current encryption\n * config: the config is enabled and its admin pubkey matches the cache's.\n * A cache left behind by a different login (or a re-enabled config) fails\n * this and must not be used — sends encrypted under it would be\n * undecryptable by every recipient device. */\n const cacheMatches = (vault: VaultContents, cfg: OrgEncryptionConfig): boolean =>\n cfg.enabled &&\n typeof cfg.adminPubkeyB64 === \"string\" &&\n sodium.constantTimeEqual(vault.adminPublicKey, sodium.fromB64(cfg.adminPubkeyB64));\n\n /** The decrypted vault: the local cache when it matches the org's current\n * config, otherwise prompt, unlock, and cache for next time. The config is\n * fetched even on the cache path — one GET per encrypted send buys the\n * staleness check above (without it, a `vault.json` surviving a re-login\n * would silently encrypt under the previous org's key). */\n const getOrPrompt = Effect.gen(function* () {\n const cached = yield* vaultStore.load;\n const cfg = yield* fetchConfig;\n if (cached._tag === \"Some\") {\n if (cacheMatches(cached.value, cfg)) return cached.value;\n yield* vaultStore.clear;\n yield* out.warn(\"cached vault doesn't match this org's encryption config (stale login?) — cleared it\");\n }\n const enabled = yield* requireEnabled(cfg);\n const { vault } = yield* unlock(enabled, \"Org encryption passphrase: \");\n yield* vaultStore.save(vault);\n return vault;\n });\n\n return {\n readPassphrase,\n fetchConfig,\n requireEnabled,\n getOrPrompt,\n\n /** The auto-encrypt decision for org sends: the unlocked vault when the\n * org has encryption (prompting inline on a fresh machine rather than\n * silently going plaintext), `undefined` when encryption is disabled or\n * the caller opted out with --no-encrypt. */\n forSendOrPlaintext: (noEncrypt: boolean) =>\n noEncrypt\n ? Effect.succeed<VaultContents | undefined>(undefined)\n : getOrPrompt.pipe(\n Effect.map((vault): VaultContents | undefined => vault),\n Effect.catchTag(\"EncryptionDisabled\", () => Effect.succeed<VaultContents | undefined>(undefined)),\n ),\n\n /** Rotation needs `vault_key` itself (not just the decrypted contents) to\n * re-encrypt the updated vault, and the cache deliberately doesn't store\n * it — so this ALWAYS re-prompts, which is also defensible on its own\n * merits for a privileged op. */\n unlockForRotation: Effect.gen(function* () {\n const cfg = yield* fetchConfig.pipe(Effect.flatMap(requireEnabled));\n return yield* unlock(cfg, \"Org encryption passphrase (required for rotation): \");\n }),\n } as const;\n }),\n}) {}\n\nexport type { VaultContents };\n","// Shared `--topic`, `--api-token`, etc. that every subcommand can take.\n// Env fallbacks ($SP_API_TOKEN, $SP_BASE_URL, $SP_TAG) are\n// wired declaratively via `Options.withFallbackConfig`, and value validation\n// happens at parse time via `Options.mapTryCatch` — a bad `--password` fails\n// as a usage error before any handler runs.\n\nimport { HelpDoc, Options } from \"@effect/cli\";\nimport { Config, Effect, Option } from \"effect\";\n\nimport { MissingApiToken } from \"./errors.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.simplepu.sh\";\n\nconst toHelp = (e: unknown): HelpDoc.HelpDoc => HelpDoc.p(e instanceof Error ? e.message : String(e));\n\nexport const topicOption = Options.text(\"topic\").pipe(\n Options.withAlias(\"t\"),\n Options.withDescription(\"Topic to send to (`task`) or filter on (`events`, repeatable). Omit on `task` for a self-send to your own devices.\"),\n Options.repeated,\n);\n\nexport const apiTokenOption = Options.text(\"api-token\").pipe(\n Options.withDescription(\n \"API token. Required for `get`; `collect`, `events`, and `subtask` fall back to the logged-in org session when omitted. Defaults to $SP_API_TOKEN.\",\n ),\n Options.withFallbackConfig(Config.string(\"SP_API_TOKEN\")),\n Options.optional,\n);\n\n/** Personal-credential commands need the token; fail typed when absent. */\nexport const requireApiToken = (token: Option.Option<string>): Effect.Effect<string, MissingApiToken> =>\n Option.match(token, {\n onNone: () => Effect.fail(new MissingApiToken()),\n onSome: Effect.succeed,\n });\n\n/** A `--password` value: `password@topic` is a topic password (split on the\n * LAST `@`, so the password itself may contain `@`); a bare value is the\n * account default password. The SDK rejects more than one default. */\nexport type PasswordFlag = [password: string, topic: string] | string;\n\nexport function parsePasswordFlag(value: string): PasswordFlag {\n const at = value.lastIndexOf(\"@\");\n if (at === -1) return value; // bare → account default password\n const password = value.slice(0, at);\n const topic = value.slice(at + 1);\n if (!password || !topic) {\n throw new Error(\n `invalid --password \\`${value}\\`: use \\`password@topic\\` for a topic password, or a bare password for the account default`,\n );\n }\n return [password, topic];\n}\n\nexport const passwordOption = Options.text(\"password\").pipe(\n Options.withAlias(\"p\"),\n Options.withDescription(\n \"End-to-end encryption password. `password@topic` sets a topic's password \" +\n \"(encrypts sends to it and decrypts its content); a bare `password` is your \" +\n \"account default (decrypts your submissions). Repeatable.\",\n ),\n Options.repeated,\n // Per-value validation happens on the collected array — `repeated` only\n // composes on a bare option, so the map goes after it.\n Options.mapTryCatch((values) => values.map(parsePasswordFlag), toHelp),\n);\n\n/** True when the SDK will E2E-encrypt a send: a `password@topic` pair matching\n * the topic, or (for a note-to-self) a bare account-default password. */\nexport const willEncrypt = (passwords: ReadonlyArray<PasswordFlag>, topic: string | undefined): boolean =>\n topic !== undefined\n ? passwords.some((p) => Array.isArray(p) && p[1] === topic)\n : passwords.some((p) => typeof p === \"string\");\n\nexport const baseUrlOption = Options.text(\"base-url\").pipe(\n Options.withDescription(`API base URL. Defaults to $SP_BASE_URL or ${DEFAULT_BASE_URL}.`),\n Options.withFallbackConfig(Config.string(\"SP_BASE_URL\")),\n Options.withDefault(DEFAULT_BASE_URL),\n);\n\nexport const quietOption = Options.boolean(\"quiet\").pipe(\n Options.withAlias(\"q\"),\n Options.withDescription(\"Suppress informational output, only print payloads.\"),\n);\n\n/** Shared helper for repeatable validated text options. */\nexport const mappedText = <B>(name: string, parse: (raw: string) => B) =>\n Options.text(name).pipe(Options.mapTryCatch(parse, toHelp));\n","// Tiny presentation helpers shared across commands.\n\nexport function formatInstant(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return iso;\n return d.toISOString().replace(\"T\", \" \").replace(/\\..+/, \" UTC\");\n}\n\nexport function maskToken(t: string): string {\n if (t.length <= 8) return \"*\".repeat(t.length);\n return `${t.slice(0, 4)}…${t.slice(-4)}`;\n}\n","// `sp auth` — login / logout / status.\n//\n// Login has two browser flows that both end in a long-lived cli_session token saved to\n// ~/.config/simplepush/auth.json (mode 0600):\n//\n// - Localhost-callback (default on a desktop): start a one-shot HTTP server on a\n// random port, open <base-url>/cli/auth/approve?redirect_uri=...&state=..., wait for\n// the browser to redirect the auth code back (a Deferred), exchange it for the token.\n// - Device flow (default over SSH / headless; RFC 8628 style): POST device/start, show\n// a short user_code, the admin approves it in any browser, the CLI polls device/token\n// until the token is minted. No loopback server, so the browser and CLI need not share\n// a machine.\n//\n// The flow is auto-selected (see preferDeviceFlow) and overridable with --device / --web.\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Deferred, Duration, Effect, Exit, Option, Redacted, Schema } from \"effect\";\nimport { createServer } from \"node:http\";\nimport { spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport type { AddressInfo } from \"node:net\";\n\nimport { baseUrlOption, quietOption } from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { AuthStore, InviteStore, VaultStore, bearerToken } from \"../services/stores.js\";\nimport { maskToken } from \"../format.js\";\n\nconst LOGIN_TIMEOUT = Duration.minutes(5);\n\nconst successPage = `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Logged in</title>\n<style>body{font-family:-apple-system,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0;color:#1d1d1f;}\n@media (prefers-color-scheme:dark){body{background:#1c1c1e;color:#f5f5f7;}}</style></head>\n<body><div><h1 style=\"font-weight:600;\">Logged in</h1><p>You can close this tab and return to your terminal.</p></div></body></html>`;\n\nconst errorPage = (msg: string) => `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>Error</title></head>\n<body style=\"font-family:-apple-system,sans-serif;padding:2rem;\"><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></body></html>`;\n\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) =>\n ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" })[c] ?? c,\n );\n}\n\n// The server returns the plaintext API key exactly once — on the exchange that\n// bootstrapped it. `lastRotatedAt` is absent (not null) until the key's first\n// rotation — zio-json omits None.\nconst ExchangePayload = Schema.Struct({\n token: Schema.String,\n apiKey: Schema.optional(Schema.NullOr(Schema.String)),\n apiKeyInfo: Schema.Struct({\n prefix: Schema.String,\n createdAt: Schema.String,\n lastRotatedAt: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n});\ntype ExchangePayload = typeof ExchangePayload.Type;\n\nconst DeviceStartResponse = Schema.Struct({\n deviceCode: Schema.String,\n userCode: Schema.String,\n expiresIn: Schema.Number,\n interval: Schema.Number,\n});\n\nconst DeviceTokenError = Schema.Struct({ error: Schema.optional(Schema.String) });\n\nconst decodeJson = <A, I>(schema: Schema.Schema<A, I>) => Schema.decodeUnknown(Schema.parseJson(schema));\n\ninterface CallbackResult {\n readonly code: string;\n readonly state: string;\n}\n\n/** One-shot loopback server: resolves the Deferred with the redirected\n * code+state. Scoped — releasing tears down the server AND its keep-alive\n * sockets (Connection: close + closeAllConnections; without both, the process\n * would hang on the browser's held-open socket). */\nconst startCallbackServer = Effect.gen(function* () {\n const callback = yield* Deferred.make<CallbackResult, UserError>();\n\n const noKeepAlive = { \"Content-Type\": \"text/html; charset=utf-8\", Connection: \"close\" };\n const server = yield* Effect.acquireRelease(\n Effect.sync(() =>\n createServer((req, res) => {\n const path = req.url ?? \"/\";\n if (!path.startsWith(\"/callback\")) {\n res.writeHead(404, { Connection: \"close\" }).end();\n return;\n }\n const url = new URL(path, \"http://localhost\");\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n if (!code || !state) {\n res.writeHead(400, noKeepAlive).end(errorPage(\"Missing code or state.\"));\n Deferred.unsafeDone(callback, Exit.fail(new UserError({ message: \"callback missing code or state\" })));\n return;\n }\n res.writeHead(200, noKeepAlive).end(successPage);\n Deferred.unsafeDone(callback, Exit.succeed({ code, state }));\n }),\n ),\n (server) =>\n Effect.sync(() => {\n server.closeAllConnections();\n server.close();\n }),\n );\n\n const port = yield* Effect.async<number, UserError>((resume) => {\n server.once(\"error\", (e) => resume(Effect.fail(new UserError({ message: `callback server failed: ${e.message}` }))));\n server.listen(0, \"127.0.0.1\", () => resume(Effect.succeed((server.address() as AddressInfo).port)));\n });\n\n return { port, awaitCallback: Deferred.await(callback) } as const;\n});\n\nconst openInBrowser = (url: string) =>\n Effect.sync(() => {\n const cmd =\n process.platform === \"darwin\" ? \"open\"\n : process.platform === \"win32\" ? \"cmd\"\n : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n try {\n const child = spawn(cmd, args, { detached: true, stdio: \"ignore\" });\n child.unref();\n } catch {\n // If spawn fails (rare — `open` etc. missing), the user can copy the URL from stderr.\n }\n });\n\n// Headless / remote machines have no local browser that can receive a localhost\n// callback, so prefer the device flow there. Overridable with --device / --web.\nfunction preferDeviceFlow(): boolean {\n if (process.env.SP_AUTH_DEVICE === \"1\") return true;\n if (process.env.SSH_CONNECTION || process.env.SSH_TTY) return true;\n if (process.platform === \"linux\" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) return true;\n return false;\n}\n\n// Shared tail for both flows: persist the session token and surface the org API key.\nconst finishLogin = (baseUrl: string, payload: ExchangePayload) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const store = yield* AuthStore;\n\n if (!payload.token) return yield* Effect.fail(new UserError({ message: \"exchange returned empty token\" }));\n\n const path = yield* store.save({\n baseUrl,\n token: Redacted.make(payload.token),\n loggedInAt: new Date().toISOString(),\n });\n\n yield* out.info(`saved credentials to ${path}`);\n yield* out.print(\"Logged in.\");\n\n // The plaintext API key appears exactly once, on the bootstrapping\n // exchange. Surface it on stdout so the user can copy it; we do not\n // persist it locally (the CLI authenticates with the session token).\n if (payload.apiKey) {\n yield* out.print(\"\\nOrganization API key (shown once, copy now):\");\n yield* out.print(` ${payload.apiKey}`);\n } else {\n yield* out.print(\n `\\nOrganization API key already provisioned (prefix ${payload.apiKeyInfo.prefix}…).\\n` +\n \"Run `simplepush org api-key rotate` to surface a fresh one.\",\n );\n }\n });\n\nconst runLocalhostFlow = (baseUrl: string) =>\n Effect.scoped(\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n\n const state = randomBytes(16).toString(\"hex\");\n const { port, awaitCallback } = yield* startCallbackServer;\n const redirectUri = `http://127.0.0.1:${port}/callback`;\n const authUrl =\n `${baseUrl}/cli/auth/approve?redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(state)}`;\n\n yield* out.info(`opening browser: ${authUrl}`);\n yield* out.info(\"(if it didn't open, paste that URL into your browser)\");\n yield* openInBrowser(authUrl);\n\n const callback = yield* awaitCallback.pipe(\n Effect.timeoutFail({\n duration: LOGIN_TIMEOUT,\n onTimeout: () => new UserError({ message: `login timed out after ${Duration.toSeconds(LOGIN_TIMEOUT)}s` }),\n }),\n );\n if (callback.state !== state) {\n return yield* Effect.fail(new UserError({ message: \"state mismatch — refusing to exchange (possible CSRF)\" }));\n }\n\n yield* out.info(\"approved, exchanging code...\");\n\n const res = yield* api.unauthedPost(\"exchange\", `${baseUrl}/cli/auth/exchange`, { code: callback.code });\n if (res.status >= 400) {\n return yield* Effect.fail(new UserError({ message: `exchange failed: ${res.status} ${res.body}` }));\n }\n const payload = yield* decodeJson(ExchangePayload)(res.body);\n yield* finishLogin(baseUrl, payload);\n }),\n );\n\nconst runDeviceFlow = (baseUrl: string) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n\n const startRes = yield* api.unauthedPost(\"device start\", `${baseUrl}/cli/auth/device/start`);\n if (startRes.status >= 400) {\n return yield* Effect.fail(new UserError({ message: `device start failed: ${startRes.status} ${startRes.body}` }));\n }\n const start = yield* decodeJson(DeviceStartResponse)(startRes.body);\n\n // The verification page lives on the same host the CLI talks to (mirrors how\n // the localhost flow builds the approve URL), so construct it from baseUrl.\n const verifyUrl = `${baseUrl}/cli/auth/device`;\n const verifyUrlComplete = `${verifyUrl}?user_code=${encodeURIComponent(start.userCode)}`;\n\n yield* out.print(`\\nTo authorize this CLI, open:\\n ${verifyUrl}`);\n yield* out.print(`and enter the code:\\n ${start.userCode}\\n`);\n // On a machine with a browser, open it straight to the pre-filled approve page.\n if (!preferDeviceFlow()) yield* openInBrowser(verifyUrlComplete);\n yield* out.info(\"waiting for approval... (Ctrl-C to cancel)\");\n\n // Recursive poll: sleep, POST, branch on the RFC 8628 error code.\n // `slow_down` stretches the interval; the whole loop races the code expiry.\n const poll = (intervalSeconds: number): Effect.Effect<ExchangePayload, UserError, Api> =>\n Effect.gen(function* () {\n yield* Effect.sleep(Duration.seconds(intervalSeconds));\n const res = yield* (yield* Api).unauthedPost(\"device token\", `${baseUrl}/cli/auth/device/token`, {\n deviceCode: start.deviceCode,\n }).pipe(Effect.mapError((e) => new UserError({ message: `device token exchange failed: ${String(e.cause)}` })));\n if (res.status < 400) {\n return yield* decodeJson(ExchangePayload)(res.body).pipe(\n Effect.mapError(() => new UserError({ message: \"device token exchange returned an unreadable payload\" })),\n );\n }\n const err = yield* decodeJson(DeviceTokenError)(res.body).pipe(Effect.orElseSucceed(() => ({ error: undefined })));\n switch (err.error) {\n case \"authorization_pending\":\n return yield* poll(intervalSeconds);\n case \"slow_down\":\n return yield* poll(intervalSeconds + 5);\n case \"access_denied\":\n return yield* Effect.fail(new UserError({ message: \"authorization was denied\" }));\n case \"expired_token\":\n return yield* Effect.fail(new UserError({ message: \"the code expired — run `sp auth login` again\" }));\n default:\n return yield* Effect.fail(new UserError({ message: `device token exchange failed: ${res.status}` }));\n }\n });\n\n const payload = yield* poll(start.interval > 0 ? start.interval : 5).pipe(\n Effect.timeoutFail({\n duration: Duration.seconds(start.expiresIn),\n onTimeout: () => new UserError({ message: \"device authorization timed out\" }),\n }),\n );\n yield* finishLogin(baseUrl, payload);\n });\n\nconst deviceFlagOption = Options.boolean(\"device\").pipe(\n Options.withDescription(\"Use the device-code flow (for SSH / headless machines). Auto-selected when no local browser is detected.\"),\n);\nconst webFlagOption = Options.boolean(\"web\").pipe(\n Options.withDescription(\"Use the localhost-callback browser flow (default on a desktop with a browser).\"),\n);\n\nconst authLogin = Command.make(\n \"login\",\n {\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n device: deviceFlagOption,\n web: webFlagOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const baseUrl = args[\"base-url\"].replace(/\\/+$/, \"\");\n\n // --device / --web force a flow; otherwise auto-detect (device on SSH/headless).\n const useDevice = args.device || (!args.web && preferDeviceFlow());\n yield* useDevice ? runDeviceFlow(baseUrl) : runLocalhostFlow(baseUrl);\n // No explicit process.exit here: the Effect runtime's teardown exits the\n // process, so undici's keep-alive sockets can't hold the event loop open.\n }),\n);\n\nconst authLogout = Command.make(\"logout\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const auth = yield* AuthStore;\n const removed = yield* auth.clear;\n // Logout also wipes the org-encryption local state — the cached plaintext\n // vault and any issued invite codes — because both are bound to the\n // now-logged-out admin's session. Without this, a subsequent `login` as a\n // different admin would inherit the previous admin's vault cache and\n // invite list.\n yield* (yield* VaultStore).clear;\n yield* (yield* InviteStore).clear;\n // Logout only purges the local file — there is no server-side revocation,\n // so the session itself remains valid on the server.\n yield* out.print(removed ? `Logged out (deleted ${auth.filePath}).` : `No saved credentials at ${auth.filePath}.`);\n }),\n);\n\nconst authStatus = Command.make(\"status\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const store = yield* AuthStore;\n const auth = yield* store.load;\n if (Option.isNone(auth)) {\n yield* out.print(\"Not logged in. Run `sp auth login` to authenticate.\");\n return yield* Effect.fail(new Aborted());\n }\n yield* out.print(\"Logged in.\");\n yield* out.print(` base url: ${auth.value.baseUrl}`);\n yield* out.print(` token: ${maskToken(bearerToken(auth.value))}`);\n yield* out.print(` since: ${auth.value.loggedInAt}`);\n yield* out.print(` file: ${store.filePath}`);\n }),\n);\n\nexport const authCommand = Command.make(\"auth\").pipe(\n Command.withSubcommands([authLogin, authLogout, authStatus]),\n);\n","// The Effect ↔ SDK boundary. `@simplepush/sdk` is promise/async-iterable\n// based; everything crossing that line goes through these three combinators so\n// the rest of the CLI never touches a bare Promise.\n\nimport { Client, OrgClient } from \"@simplepush/sdk\";\nimport { Effect, Scope, Stream } from \"effect\";\n\nimport { SdkFailure } from \"../errors.js\";\n\ntype ClientConfig = ConstructorParameters<typeof Client>[0];\ntype OrgClientConfig = ConstructorParameters<typeof OrgClient>[0];\n\n/** A Client whose websocket/http resources are released with the scope. */\nexport const acquireClient = (config: ClientConfig): Effect.Effect<Client, never, Scope.Scope> =>\n Effect.acquireRelease(\n Effect.sync(() => new Client(config)),\n (client) => Effect.promise(async () => client.close()).pipe(Effect.ignore),\n );\n\n/** An OrgClient (Api-Key or CLI-session bearer) scoped like `acquireClient`. */\nexport const acquireOrgClient = (config: OrgClientConfig): Effect.Effect<OrgClient, never, Scope.Scope> =>\n Effect.acquireRelease(\n Effect.sync(() => new OrgClient(config)),\n (client) => Effect.promise(async () => client.close()).pipe(Effect.ignore),\n );\n\n/** One promise-returning SDK call as a typed Effect. */\nexport const sdkCall = <A>(action: string, f: () => Promise<A>): Effect.Effect<A, SdkFailure> =>\n Effect.tryPromise({ try: f, catch: (cause) => new SdkFailure({ action, cause }) });\n\n/** An SDK async-iterable as a Stream.\n *\n * Termination MUST go through the AbortSignal, never a bare generator\n * `.return()`: the SDK's reconnect backoff only unblocks on signal-abort, so a\n * plain return() deadlocks against the backoff sleep. The iterator wrapper\n * below aborts FIRST (which breaks the sleep), then lets the generator's own\n * cleanup run. The controller is also aborted by the scope finalizer, covering\n * interruption (timeouts, races, Ctrl-C). */\nexport const sdkStream = <A>(\n action: string,\n make: (signal: AbortSignal) => AsyncIterable<A>,\n): Stream.Stream<A, SdkFailure> =>\n Stream.unwrapScoped(\n Effect.map(\n Effect.acquireRelease(\n Effect.sync(() => new AbortController()),\n (controller) => Effect.sync(() => controller.abort()),\n ),\n (controller) =>\n Stream.fromAsyncIterable(\n abortFirst(make(controller.signal), controller),\n (cause) => new SdkFailure({ action, cause }),\n ),\n ),\n );\n\nfunction abortFirst<A>(iterable: AsyncIterable<A>, controller: AbortController): AsyncIterable<A> {\n return {\n [Symbol.asyncIterator]() {\n const it = iterable[Symbol.asyncIterator]();\n return {\n next: () => it.next(),\n return: async () => {\n controller.abort();\n try {\n await it.return?.();\n } catch {\n // The generator surfacing its own abort is expected here.\n }\n return { done: true as const, value: undefined };\n },\n throw: (e?: unknown) =>\n it.throw?.(e) ?? Promise.reject(e instanceof Error ? e : new Error(String(e))),\n };\n },\n };\n}\n","// Where the per-credential broker socket lives. One daemon (one upstream WS)\n// serves every `sp` process sharing the same stream identity — the credential\n// KIND (personal API-Token stream vs org-session stream), the secret, and the\n// base URL — so the path is keyed by a hash of all three, never the secret\n// itself (it must not leak into a world-readable path). Under the user's\n// runtime/temp dir, which is user-owned.\n\nimport { createHash } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** The credential a broker holds — which upstream endpoint it dials and what\n * it authenticates with. `personal` streams `/ws/v1/events` by API-Token; `org`\n * streams `/ws/v1/events/organization` by the CLI-session bearer. Keying the\n * socket by the bearer means a re-login gets a fresh broker (the old session\n * may be revoked); the stale broker idle-exits on its own. */\nexport type BrokerCredential =\n | { kind: \"personal\"; apiToken: string }\n | { kind: \"org\"; bearer: string };\n\n/** Directory for broker sockets. Prefer `$XDG_RUNTIME_DIR` (0700, user-only,\n * tmpfs) when set; otherwise the OS temp dir. */\nexport function daemonDir(): string {\n const xdg = process.env.XDG_RUNTIME_DIR;\n return xdg && xdg.length > 0 ? join(xdg, \"simplepush\") : join(tmpdir(), \"simplepush\");\n}\n\n/** Socket path for a given credential. The hash covers the credential kind,\n * the secret AND the base URL so two accounts, an account vs its org session,\n * or prod vs a local backend never collide on one broker. The kind also\n * appears in the filename for debuggability. */\nexport function daemonSocketPath(credential: BrokerCredential, baseUrl: string): string {\n const secret = credential.kind === \"personal\" ? credential.apiToken : credential.bearer;\n const key = createHash(\"sha256\").update(`${credential.kind}\\n${baseUrl}\\n${secret}`).digest(\"hex\").slice(0, 16);\n return join(daemonDir(), `events-${credential.kind}-${key}.sock`);\n}\n","// The broker: one long-lived process holding a SINGLE upstream events WS\n// connection for a credential — the personal `/ws/v1/events` stream by\n// API-Token, or the org-wide `/ws/v1/events/organization` stream by the CLI\n// session bearer — fanning every event out to the local `sp` processes\n// attached over its Unix socket. Short-lived `sp collect` / `sp events`\n// invocations attach to this instead of each dialing the backend, collapsing N\n// duplicate server-side streams to one.\n//\n// The broker is dumb transport: it forwards RAW (still-encrypted) event frames\n// and holds only the credential (to open the WS). Decryption keys never reach it\n// — each attached CLI decrypts locally. It keeps a bounded ring of recent events\n// so a just-attached client catches the last moments (covers the send→collect\n// gap), maintains the reconnect/resume cursor centrally, and idle-exits once the\n// last client leaves.\n//\n// Structure: a PubSub fans upstream events out to one fiber per attached\n// client; a SubscriptionRef counts clients and drives the debounced idle-exit;\n// the whole broker is one Scope, so socket file, server, client fibers, and\n// the upstream websocket all unwind together whichever of the three racers\n// (upstream end, idle, Ctrl-C interrupt) wins.\n\nimport { createConnection } from \"node:net\";\nimport { FileSystem, Socket } from \"@effect/platform\";\nimport { NodeSocketServer } from \"@effect/platform-node\";\nimport type { Event } from \"@simplepush/sdk\";\nimport { Chunk, Deferred, Duration, Effect, Fiber, Option, PubSub, Ref, Schedule, Stream, SubscriptionRef } from \"effect\";\n\nimport { daemonDir, daemonSocketPath, type BrokerCredential } from \"./paths.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { acquireClient, acquireOrgClient, sdkStream } from \"../services/sdk.js\";\n\nconst RING_MAX = 1000;\nconst IDLE_EXIT = \"60 seconds\";\n// The broker opens its upstream with this much lookback so a broker auto-spawned\n// by the FIRST `sp collect` still backfills a send that just happened — a client\n// attaching with `since = send time` is served from the ring, not missed.\nconst UPSTREAM_LOOKBACK_MS = 10 * 60_000;\n\n/** True if a broker is already listening on `path` (so we don't double-bind). */\nexport const probeDaemon = (path: string, timeoutMs = 500): Effect.Effect<boolean> =>\n Effect.async<boolean>((resume) => {\n const sock = createConnection(path);\n const done = (live: boolean) => {\n sock.destroy();\n resume(Effect.succeed(live));\n };\n const timer = setTimeout(() => done(false), timeoutMs);\n sock.once(\"connect\", () => { clearTimeout(timer); done(true); });\n sock.once(\"error\", () => { clearTimeout(timer); done(false); });\n return Effect.sync(() => { clearTimeout(timer); sock.destroy(); });\n });\n\n/** Wait for a just-spawned broker to come up. */\nexport const waitForDaemon = (path: string, timeout: Duration.DurationInput): Effect.Effect<boolean> =>\n probeDaemon(path, 200).pipe(\n Effect.filterOrFail((up) => up, () => \"not-up\" as const),\n Effect.retry(Schedule.spaced(\"100 millis\")),\n Effect.timeoutOption(timeout),\n Effect.map(Option.isSome),\n Effect.orElseSucceed(() => false),\n );\n\nconst parseSubscribeLine = (line: string): string | undefined => {\n try {\n return (JSON.parse(line) as { since?: string }).since;\n } catch {\n return undefined; // blank / malformed subscribe line → full ring\n }\n};\n\n/** Run the broker for a credential until the upstream dies or it idle-exits.\n * Resolves when the broker has fully shut down. */\nexport const runDaemon = (opts: { credential: BrokerCredential; baseUrl: string }) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const fs = yield* FileSystem.FileSystem;\n const path = daemonSocketPath(opts.credential, opts.baseUrl);\n\n yield* fs.makeDirectory(daemonDir(), { recursive: true }).pipe(Effect.ignore);\n yield* fs.chmod(daemonDir(), 0o700).pipe(Effect.ignore);\n\n // If a live broker already owns this socket, defer to it; a stale socket\n // file (crashed predecessor) is removed so we can bind.\n if (yield* probeDaemon(path)) {\n return yield* out.info(\"a broker is already running for this credential; exiting\");\n }\n yield* fs.remove(path).pipe(Effect.ignore);\n\n const reason = yield* Effect.scoped(\n Effect.gen(function* () {\n const server = yield* NodeSocketServer.make({ path });\n yield* Effect.addFinalizer(() => fs.remove(path).pipe(Effect.ignore));\n yield* fs.chmod(path, 0o600).pipe(Effect.ignore);\n\n const events = yield* PubSub.unbounded<Event>();\n const ring = yield* Ref.make(Chunk.empty<Event>());\n const clients = yield* SubscriptionRef.make(0);\n\n const handleClient = (socket: Socket.Socket) =>\n Effect.scoped(\n Effect.gen(function* () {\n yield* SubscriptionRef.update(clients, (n) => n + 1);\n yield* Effect.addFinalizer(() => SubscriptionRef.update(clients, (n) => n - 1));\n\n const write = yield* socket.writer;\n\n // The client may send one subscribe line `{\"since\":\"…\"}` to request\n // backlog from a point (the send time); anything before the first\n // newline is it. The reader keeps draining afterwards (we ignore\n // the rest) and completes when the peer disconnects.\n const firstLine = yield* Deferred.make<string>();\n const readState = { buf: \"\", done: false };\n const reader = yield* Effect.fork(\n socket.run((data) => {\n if (readState.done) return;\n readState.buf += Buffer.from(data).toString(\"utf8\");\n const nl = readState.buf.indexOf(\"\\n\");\n if (nl === -1) return;\n readState.done = true;\n return Deferred.succeed(firstLine, readState.buf.slice(0, nl));\n }),\n );\n\n // Subscribe BEFORE snapshotting the ring: no event can fall in the\n // gap between backlog and live. An event published between the two\n // shows up in both — `seen` drops that duplicate exactly once.\n const dequeue = yield* PubSub.subscribe(events);\n\n // A client that never sends a subscribe line still goes live after\n // a short grace period, so a bare connection gets the stream.\n const line = yield* Deferred.await(firstLine).pipe(Effect.timeoutOption(\"100 millis\"));\n const since = Option.match(line, { onNone: () => undefined, onSome: parseSubscribeLine });\n\n const snapshot = Chunk.toReadonlyArray(yield* Ref.get(ring));\n const backlog = since === undefined\n ? snapshot\n : snapshot.filter((e) => e.createdAt !== undefined && e.createdAt >= since);\n const seen = new Set<Event>(snapshot);\n for (const ev of backlog) yield* write(JSON.stringify(ev) + \"\\n\");\n\n const live = Stream.fromQueue(dequeue).pipe(\n Stream.filterEffect((ev) => Effect.sync(() => !seen.delete(ev))),\n Stream.runForEach((ev) => write(JSON.stringify(ev) + \"\\n\")),\n );\n\n // Serve until the peer hangs up (reader completes/fails) or a\n // write fails; either way the scope drops the subscription.\n yield* Effect.raceFirst(live, Fiber.join(reader));\n }),\n ).pipe(Effect.catchAllCause(() => Effect.void)); // one bad client never kills the broker\n\n const acceptLoop = server.run(handleClient);\n\n // Idle exit: shut down once the client count has sat at zero for a full\n // idle window (the debounce timer resets on every attach/detach).\n const idleExit = clients.changes.pipe(\n Stream.debounce(IDLE_EXIT),\n Stream.filter((n) => n === 0),\n Stream.take(1),\n Stream.runDrain,\n Effect.as(\"idle, no clients\"),\n );\n\n // The single upstream connection — the flavor matching the credential\n // (`events()` dials the right endpoint with the right auth header on\n // both client classes). Any terminal condition tears the broker down\n // (clients see their socket close and fall back / end).\n const upstream = Effect.scoped(\n Effect.gen(function* () {\n const client =\n opts.credential.kind === \"personal\"\n ? yield* acquireClient({ baseUrl: opts.baseUrl, apiToken: opts.credential.apiToken })\n : yield* acquireOrgClient({ baseUrl: opts.baseUrl, bearerToken: opts.credential.bearer });\n const since = new Date(Date.now() - UPSTREAM_LOOKBACK_MS).toISOString();\n yield* sdkStream(\"upstream events\", (signal) => client.events({ since, signal })).pipe(\n Stream.runForEach((ev) =>\n Ref.update(ring, (r) => {\n const next = Chunk.append(r, ev);\n return Chunk.size(next) > RING_MAX ? Chunk.drop(next, 1) : next;\n }).pipe(Effect.zipRight(PubSub.publish(events, ev))),\n ),\n );\n return \"upstream closed\";\n }),\n ).pipe(\n Effect.catchTag(\"SdkFailure\", (e) =>\n out\n .warn(`upstream events stream failed: ${e.cause instanceof Error ? e.cause.message : String(e.cause)}`)\n .pipe(Effect.as(\"upstream error\")),\n ),\n );\n\n yield* out.info(`broker listening at ${path}`);\n return yield* Effect.raceAll([upstream, idleExit, acceptLoop]);\n }),\n );\n\n yield* out.info(`broker shut down (${reason})`);\n });\n","// Client side of the broker: a `WebSocketFactory` that, instead of dialing the\n// backend, connects to the local broker socket and presents the SDK's\n// `SimplepushWebSocket`. The SDK's stream/hub then read events off the shared\n// broker connection, unchanged — injected through the SDK's\n// `CommonConfig.webSocketFactory` seam.\n//\n// The factory the SDK consumes is necessarily a plain callback (it lives on\n// the far side of the Effect boundary); everything the CLI itself drives —\n// probing, spawning, waiting — is Effect.\n\nimport { connect, type Socket } from \"node:net\";\nimport { spawn } from \"node:child_process\";\nimport type { WebSocketFactory, SimplepushWebSocket } from \"@simplepush/sdk\";\nimport { Effect, Option } from \"effect\";\n\nimport { daemonSocketPath, type BrokerCredential } from \"./paths.js\";\nimport { probeDaemon, waitForDaemon } from \"./server.js\";\nimport { CliOutput } from \"../services/output.js\";\n\n/** Adapt a connected Unix socket carrying newline-delimited event JSON into a\n * `SimplepushWebSocket`. The broker only ever sends us event frames, so\n * `messages()` yields each line verbatim. */\nfunction adaptSocket(socket: Socket, since: string | undefined): SimplepushWebSocket {\n type Frame = { kind: \"msg\"; text: string } | { kind: \"end\" } | { kind: \"error\"; err: Error };\n const queue: Frame[] = [];\n let waiter: ((f: Frame) => void) | null = null;\n const push = (f: Frame) => { if (waiter) { const w = waiter; waiter = null; w(f); } else queue.push(f); };\n\n let closed = false;\n let resolveClosed!: () => void;\n const closedPromise = new Promise<void>((res) => { resolveClosed = res; });\n\n socket.once(\"connect\", () => {\n // Request backlog from the send time so the send→collect gap isn't missed.\n socket.write(JSON.stringify({ since }) + \"\\n\");\n });\n let buf = \"\";\n socket.on(\"data\", (chunk) => {\n buf += chunk.toString(\"utf8\");\n let nl: number;\n while ((nl = buf.indexOf(\"\\n\")) !== -1) {\n const ln = buf.slice(0, nl);\n buf = buf.slice(nl + 1);\n if (ln.trim().length > 0) push({ kind: \"msg\", text: ln });\n }\n });\n socket.on(\"close\", () => { if (!closed) { closed = true; push({ kind: \"end\" }); resolveClosed(); } });\n socket.on(\"error\", (err) => push({ kind: \"error\", err }));\n\n async function* messages(): AsyncIterableIterator<string> {\n while (true) {\n const next = queue.length > 0 ? queue.shift()! : await new Promise<Frame>((res) => { waiter = res; });\n if (next.kind === \"msg\") yield next.text;\n else if (next.kind === \"end\") return;\n else throw next.err;\n }\n }\n\n return { closed: closedPromise, messages, close: () => { try { socket.destroy(); } catch { /* ignore */ } } };\n}\n\n/** A factory that connects to the broker at `path`. The SDK passes the ws url\n * (with `?since=`), which we forward to the broker as the backlog cursor. */\nfunction daemonWebSocketFactory(path: string): WebSocketFactory {\n return (url) => {\n let since: string | undefined;\n try { since = new URL(url).searchParams.get(\"since\") ?? undefined; } catch { /* leave undefined */ }\n return adaptSocket(connect(path), since);\n };\n}\n\n/** Spawn the broker detached; credentials/base-url go via env, NOT argv, so\n * they don't show up in `ps`. Org mode passes the session bearer explicitly\n * (SP_DAEMON_BEARER) rather than letting the child re-read auth.json,\n * so the child's socket path deterministically matches the one we probe. */\nconst spawnDetachedDaemon = (opts: { credential: BrokerCredential; baseUrl: string }) =>\n Effect.sync(() => {\n const credentialEnv =\n opts.credential.kind === \"personal\"\n ? { SP_API_TOKEN: opts.credential.apiToken }\n : { SP_DAEMON_BEARER: opts.credential.bearer };\n const child = spawn(process.execPath, [process.argv[1]!, \"daemon\"], {\n detached: true,\n stdio: \"ignore\",\n env: { ...process.env, ...credentialEnv, SP_BASE_URL: opts.baseUrl },\n });\n child.unref();\n });\n\n/** Resolve the WebSocket transport for a shared-mode client: attach to a running\n * broker, auto-spawning one (detached) if absent. Returns `Option.none` to fall\n * back to a direct connection when the broker can't be reached — the broker is\n * an optimisation and must never break `sp`. */\nexport const sharedWebSocketFactory = (opts: {\n credential: BrokerCredential;\n baseUrl: string;\n}): Effect.Effect<Option.Option<WebSocketFactory>, never, CliOutput> =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const path = daemonSocketPath(opts.credential, opts.baseUrl);\n\n const attach = Effect.gen(function* () {\n if (yield* probeDaemon(path)) {\n yield* out.info(\"attached to the shared events broker\");\n return Option.some(daemonWebSocketFactory(path));\n }\n yield* spawnDetachedDaemon(opts);\n if (!(yield* waitForDaemon(path, \"3 seconds\"))) {\n yield* out.warn(\"could not start the shared events broker; using a direct connection\");\n return Option.none<WebSocketFactory>();\n }\n yield* out.info(\"started shared events broker\");\n return Option.some(daemonWebSocketFactory(path));\n });\n\n return yield* attach.pipe(\n Effect.catchAllCause((cause) =>\n out\n .warn(`shared broker unavailable (${cause.toString()}); using a direct connection`)\n .pipe(Effect.as(Option.none<WebSocketFactory>())),\n ),\n );\n });\n","// `--since` / `--until` parsing: humantime duration (`24h`, `7d`) or ISO 8601.\n\nconst UNIT_MS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n sec: 1_000,\n secs: 1_000,\n m: 60_000,\n min: 60_000,\n mins: 60_000,\n h: 3_600_000,\n hr: 3_600_000,\n hrs: 3_600_000,\n d: 86_400_000,\n day: 86_400_000,\n days: 86_400_000,\n w: 604_800_000,\n wk: 604_800_000,\n wks: 604_800_000,\n};\n\nconst HUMANTIME_RE = /^\\s*(\\d+)\\s*([a-zA-Z]+)\\s*$/;\n\nexport function parseDurationMs(input: string): number | undefined {\n const m = HUMANTIME_RE.exec(input);\n if (!m) return undefined;\n const n = Number(m[1]);\n const unit = m[2]!.toLowerCase();\n const factor = UNIT_MS[unit];\n if (factor === undefined) return undefined;\n return n * factor;\n}\n\nexport function parseIso(input: string): Date | undefined {\n const d = new Date(input);\n return Number.isFinite(d.getTime()) ? d : undefined;\n}\n\n/** ISO 8601 string suitable for the `--since` query param. */\nexport function resolveSince(input: string): string {\n const iso = parseIso(input);\n if (iso) return iso.toISOString();\n const ms = parseDurationMs(input);\n if (ms !== undefined) return new Date(Date.now() - ms).toISOString();\n throw new Error(`could not parse \\`--since ${input}\\`: expected a duration (e.g. \\`24h\\`, \\`7d\\`) or ISO 8601 timestamp`);\n}\n\n/** ISO 8601 string for a FUTURE deadline (`--expires`): a humantime duration\n * is added to now (`2h` = two hours from now), an ISO timestamp passes\n * through. The server rejects deadlines in the past. */\nexport function resolveExpiresAt(input: string): string {\n const iso = parseIso(input);\n if (iso) return iso.toISOString();\n const ms = parseDurationMs(input);\n if (ms !== undefined) return new Date(Date.now() + ms).toISOString();\n throw new Error(`could not parse \\`--expires ${input}\\`: expected a duration (e.g. \\`2h\\`, \\`7d\\`) or ISO 8601 timestamp`);\n}\n\nexport function resolveUntil(input: string): Date {\n const iso = parseIso(input);\n if (iso) return iso;\n const ms = parseDurationMs(input);\n if (ms !== undefined) return new Date(Date.now() - ms);\n throw new Error(`could not parse \\`--until ${input}\\`: expected a duration or ISO 8601 timestamp`);\n}\n","// The unified, agent-friendly NDJSON envelope for `sp collect`. Every collected\n// item is one self-describing JSON line on stdout, flushed immediately:\n//\n// {\"type\":\"sent\",\"groupId\":…,\"createdAt\":…,\"members\":[{\"taskId\":…,\"recipient\":…}]}\n// {\"type\":\"reply\",\"groupId\":…,\"taskId\":…,\"subtaskId\":null,\"recipient\":{…},\"actor\":{…},\"body\":{…},…}\n// {\"type\":\"input\",\"taskId\":…,\"recipient\":{…},\"actor\":{…},\"inputType\":\"taskInputUploaded\",\"uploads\":[…]}\n// {\"type\":\"completed\",\"taskId\":…,\"recipient\":{…},\"actor\":{…},\"uploads\":[…]}\n// {\"type\":\"deleted\",\"taskId\":…,\"recipient\":{…},\"actor\":{…}}\n// {\"type\":\"submission\",\"id\":…,\"actor\":{…},\"body\":{…},\"photo\":{…},…}\n// {\"type\":\"end\",\"reason\":\"complete\",\"counts\":{…},\"members\":{…}}\n//\n// `recipient` is the ADDRESSING identity (static roster, who the instance was\n// sent to); `actor` is the ATTRIBUTION identity from the wire event (who\n// actually acted: public id, name/device snapshots). They coincide for\n// independent-mode instances; submissions only ever have `actor`. `actor` is\n// null on collective events (e.g. the all-recipients-deleted terminal).\n//\n// A leading `sent` line gives the group + member roster up front; a terminal\n// `end` line (always emitted) tells the agent WHEN and WHY the stream stopped,\n// so it never hangs. `pretty` is a human fallback; `json` (NDJSON) is the\n// agent contract.\n//\n// These are pure line formatters — printing is CliOutput's job.\n\nimport type { Actor, GroupActivity, GroupNotification, GroupSubtaskActivity, Submission } from \"@simplepush/sdk\";\n\nexport type CollectFormat = \"json\" | \"pretty\";\n\nexport type Recipient = { publicId: string; name: string | null };\n/** A roster member: a task instance (`tsk_…`) or a notification instance\n * (`ntf_…`). Serialized on the `sent` line under `taskId` / `notificationId`\n * respectively; internally the prefix-carrying `id` + `kind` pair. */\nexport type Member = { id: string; kind: \"task\" | \"notification\"; recipient: Recipient | null; subtaskId?: string };\n\n/** One collected group item: a task group's activity or a notification\n * group's answer. */\nexport type CollectedItem = GroupActivity | GroupNotification | GroupSubtaskActivity;\n\nexport type EndReason = \"complete\" | \"idle\" | \"count\" | \"timeout\" | \"closed\" | \"error\";\n\n/** Drops circular / non-serialisable internals from SDK view objects: `raw`\n * (the whole wire Event — circular) and `_ctx` (a download context that\n * captures the client). Download methods (`read`/`save`/`downloadUrl` when a\n * function) are omitted by JSON.stringify automatically, leaving file views as\n * their plain metadata (id / contentType / filename / size / checksum). */\nfunction cleanReplacer(key: string, value: unknown): unknown {\n if (key === \"raw\" || key === \"_ctx\") return undefined;\n return value;\n}\n\nfunction line(obj: Record<string, unknown>): string {\n return JSON.stringify(obj, cleanReplacer);\n}\n\nfunction createdAtOf(item: { createdAt?: string; raw?: { createdAt?: string } }): string | undefined {\n return item.createdAt ?? item.raw?.createdAt;\n}\n\n/** Wire-absent → null, and absent inner fields → null, per the envelope's\n * `?? null` convention. */\nfunction actorOf(item: { actor?: Actor }): Record<string, unknown> | null {\n const a = item.actor;\n if (!a) return null;\n return {\n publicId: a.publicId,\n name: a.name ?? null,\n devicePublicId: a.devicePublicId ?? null,\n deviceName: a.deviceName ?? null,\n };\n}\n\nexport function formatSent(groupId: string | undefined, createdAt: string | undefined, members: readonly Member[]): string {\n return line({\n type: \"sent\",\n groupId: groupId ?? null,\n createdAt: createdAt ?? null,\n members: members.map((m) => ({\n ...(m.kind === \"notification\" ? { notificationId: m.id } : { taskId: m.id }),\n // A subtask-append roster: the member is still the parent instance (the\n // stream demux key), scoped to this sibling subtask.\n ...(m.subtaskId !== undefined ? { subtaskId: m.subtaskId } : {}),\n recipient: m.recipient,\n })),\n });\n}\n\n/** The member instance's entity id under its kind-specific key. A subtask\n * instance keys on its PARENT task (the demux key) and stamps its own\n * `subtaskId`, so every line of a subtask collect carries both. */\nfunction instanceId(instance: CollectedItem[\"instance\"]): Record<string, string | null> {\n const inst = instance as { taskId?: string; notificationId?: string; subtaskId?: string; parentTaskId?: string };\n if (inst.subtaskId !== undefined && inst.parentTaskId !== undefined) {\n return { taskId: inst.parentTaskId, subtaskId: inst.subtaskId };\n }\n return inst.taskId !== undefined ? { taskId: inst.taskId } : { notificationId: inst.notificationId ?? null };\n}\n\n/** A single collected item — from `replies()`, `inputs()`, the combined\n * `activity()`, or a notification group's answers — into one envelope line,\n * keyed off `item.kind`. Reply, input, terminal completion, and deletion all\n * funnel through here so every collect mode emits the same schema. */\nexport function formatItem(g: CollectedItem, groupId: string | undefined): string {\n const item = g.item;\n const base = { groupId: groupId ?? null, ...instanceId(g.instance), recipient: g.recipient ?? null, actor: actorOf(item), createdAt: createdAtOf(item) ?? null };\n switch (item.kind) {\n case \"reply\":\n return line({\n type: \"reply\", ...base,\n subtaskId: item.subtaskId ?? null, id: item.id ?? null,\n body: item.body ?? null, photo: item.photo ?? null, file: item.file ?? null, audio: item.audio ?? null, location: item.location ?? null,\n });\n case \"input\":\n return line({ type: \"input\", ...base, inputType: item.type, uploads: item.uploads ?? [] });\n case \"taskCompleted\":\n return line({ type: \"completed\", ...base, uploads: item.uploads ?? [] });\n case \"subtaskCompleted\":\n // The sibling's terminal: its full committed input set (scoped — the\n // parent member stays live).\n return line({ type: \"completed\", ...base, subtaskId: item.subtaskId ?? null, uploads: item.uploads ?? [] });\n case \"notificationCompleted\":\n // The notification analogue of `completed`: the single answer, with the\n // recipient's reply (text / choice / actions) instead of input uploads.\n return line({ type: \"completed\", ...base, reply: item.reply ?? null });\n case \"taskDeleted\":\n return line({ type: \"deleted\", ...base });\n case \"taskCanceled\":\n // Sender-side withdrawal: terminal for that member. `note` is decrypted\n // where the chain key is held; `supersededBy` names the replacement task.\n return line({ type: \"canceled\", ...base, reason: item.reason ?? null, note: item.note ?? null, supersededBy: item.supersededBy ?? null });\n case \"subtaskCanceled\":\n // One follow-up withdrawn; the chain stays live (scoped, not terminal\n // for the member).\n return line({ type: \"canceled\", ...base, subtaskId: item.subtaskId ?? null, reason: item.reason ?? null, note: item.note ?? null, supersededBy: item.supersededBy ?? null });\n case \"taskDeclinedByRecipient\":\n // One recipient's \"no answer is coming from me\" — a countable SIGNAL,\n // not the member's terminal (a shared task stays live for the others).\n // `actor` (in base) says who; `note` is decrypted where its key is held.\n return line({ type: \"declined\", ...base, reason: item.reason ?? null, note: item.note ?? null });\n case \"taskDeclined\":\n // Every recipient declined — the member's terminal, the decline-side\n // mirror of `canceled`. Reasons/notes rode the preceding `declined` lines.\n return line({ type: \"all-declined\", ...base });\n case \"subtaskDeclinedByRecipient\":\n // Scoped signal: one recipient refused THIS follow-up; the chain (and\n // the member) stays live.\n return line({ type: \"declined\", ...base, subtaskId: item.subtaskId ?? null, reason: item.reason ?? null, note: item.note ?? null });\n case \"subtaskDeclined\":\n // Scoped terminal: every recipient refused this follow-up — NOT the\n // member's terminal (mirrors subtaskCanceled).\n return line({ type: \"all-declined\", ...base, subtaskId: item.subtaskId ?? null });\n case \"taskExpired\":\n // The sender-set deadline passed unanswered — the member's terminal,\n // the clock's mirror of `canceled`. Collective: no actor, no note.\n return line({ type: \"expired\", ...base });\n }\n}\n\nexport function formatSubmission(s: Submission): string {\n return line({\n type: \"submission\",\n id: s.id ?? null,\n actor: actorOf(s),\n body: s.body ?? null,\n photo: s.photo ?? null,\n file: s.file ?? null,\n audio: s.audio ?? null,\n location: s.location ?? null,\n createdAt: s.createdAt ?? null,\n });\n}\n\n// --- file views --------------------------------------------------------------\n\n/** The metadata of a downloadable file riding an item (photo/voice/file upload,\n * reply or submission photo/file/audio). `save` is the SDK-bound download\n * method; `path` is stamped by save-files.ts when `--save-files` is on (the\n * saved location, or null when the download failed). */\nexport type FileView = {\n id?: string;\n contentType?: string;\n checksumSha256?: string;\n size?: number;\n filename?: string;\n path?: string | null;\n save?: (path?: string) => Promise<string>;\n};\n\nconst isFileView = (v: unknown): v is FileView =>\n typeof v === \"object\" && v !== null && typeof (v as { save?: unknown }).save === \"function\";\n\n/** The downloadable file views an item carries: its `uploads` array (photo /\n * voice / file kinds — text/choice/… carry no `save` and drop out) or its\n * reply/submission `photo`/`file`/`audio` fields. Items with neither (deleted\n * markers, notification answers) yield []. */\nexport function fileViewsOf(item: object): FileView[] {\n const o = item as { uploads?: unknown; photo?: unknown; file?: unknown; audio?: unknown };\n const candidates = Array.isArray(o.uploads) ? o.uploads : [o.photo, o.file, o.audio];\n return candidates.filter(isFileView);\n}\n\nexport type MemberStatus = { total: number; completed: number; deleted: number; canceled: number; declined: number; expired: number; pending: number };\n\n/** The terminal line: reason the stream stopped, per-type counts, and (for\n * group modes) per-member status. Always the last line on a clean run. */\nexport function formatEnd(reason: EndReason, counts: Record<string, number>, members?: MemberStatus, errorMsg?: string): string {\n const obj: Record<string, unknown> = { type: \"end\", reason, counts };\n if (members) obj.members = members;\n if (errorMsg !== undefined) obj.error = errorMsg;\n return line(obj);\n}\n\n// --- pretty (human) fallback -------------------------------------------------\n\nfunction fmtBytes(n: number): string {\n if (n < 1024) return `${n}B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;\n return `${(n / (1024 * 1024)).toFixed(1)}MB`;\n}\n\n/** `inp_1a2b… cat.jpg 2.1MB -> files/inp_1a2b…-cat.jpg` — the id feeds\n * `sp download`, the path (when saved) locates the local copy. */\nfunction prettyFile(f: FileView): string {\n const parts = [f.id ?? \"?\"];\n if (f.filename) parts.push(f.filename);\n else if (f.contentType) parts.push(f.contentType);\n if (f.size !== undefined) parts.push(fmtBytes(f.size));\n if (f.path) parts.push(`-> ${f.path}`);\n return parts.join(\" \");\n}\n\nfunction prettyFileSuffix(item: object): string {\n const files = fileViewsOf(item);\n return files.length === 0 ? \"\" : ` files: ${files.map(prettyFile).join(\", \")}`;\n}\n\nexport function formatPrettyItem(g: CollectedItem): string {\n const a = g.item.actor;\n const inst = g.instance as { taskId?: string; notificationId?: string };\n const who = a?.name ?? g.recipient?.name ?? a?.publicId ?? g.recipient?.publicId ?? inst.taskId ?? inst.notificationId;\n const item = g.item;\n switch (item.kind) {\n case \"reply\": {\n const text = item.body?.kind === \"text\" ? item.body.text ?? \"\" : JSON.stringify(item.body);\n return ` ${who} (reply): ${text}${prettyFileSuffix(item)}`;\n }\n case \"input\": return ` ${who} [${item.type}]${prettyFileSuffix(item)}`;\n case \"taskCompleted\": return ` ${who} [completed]${prettyFileSuffix(item)}`;\n case \"subtaskCompleted\": return ` ${who} [subtask completed]${prettyFileSuffix(item)}`;\n case \"notificationCompleted\": {\n const r = item.reply;\n const answer = r === undefined ? \"\" : r.type === \"text\" ? `: ${r.value}` : r.type === \"choice\" ? `: ${r.selectedValue}` : `: ${r.selectedKey}`;\n return ` ${who} [answered]${answer}`;\n }\n case \"taskDeleted\": return ` ${who} [deleted]`;\n case \"taskCanceled\": return ` ${who} [canceled${item.reason && item.reason !== \"canceled\" ? `: ${item.reason}` : \"\"}]${item.note ? ` ${item.note}` : \"\"}`;\n case \"subtaskCanceled\": return ` ${who} [subtask canceled${item.reason && item.reason !== \"canceled\" ? `: ${item.reason}` : \"\"}]`;\n case \"taskDeclinedByRecipient\": return ` ${who} [declined${item.reason === \"failed\" ? \": failed\" : \"\"}]${item.note ? ` ${item.note}` : \"\"}`;\n case \"taskDeclined\": return ` ${who} [all declined]`;\n case \"subtaskDeclinedByRecipient\": return ` ${who} [subtask declined${item.reason === \"failed\" ? \": failed\" : \"\"}]${item.note ? ` ${item.note}` : \"\"}`;\n case \"subtaskDeclined\": return ` ${who} [subtask all declined]`;\n case \"taskExpired\": return ` ${who} [expired]`;\n }\n}\n\nexport function formatPrettySubmission(s: Submission): string {\n const text = s.body?.kind === \"text\" ? s.body.text ?? \"\" : JSON.stringify(s.body);\n const who = s.actor ? s.actor.name ?? s.actor.publicId : undefined;\n const device = s.actor?.deviceName;\n const from = who ? ` from ${who}${device ? ` (${device})` : \"\"}` : \"\";\n return ` submission${from}: ${text}${prettyFileSuffix(s)}`;\n}\n","// Local saving for the downloadable files riding collect items (photo / voice /\n// file uploads, reply files, submission files). The SDK binds `save()` to each\n// file view (checksum-verified, decrypted); this module decides WHERE each file\n// lands and stamps the outcome onto the view itself, so the NDJSON line and the\n// pretty formatter report it: `path` is the saved location, or null when the\n// download failed (the failure itself goes to the caller as a warning).\n//\n// Target names are id-prefixed (`inp_…-cat.jpg`) so files from different\n// recipients never collide, and a re-run overwrites deterministically.\n\nimport { basename, join } from \"node:path\";\n\nimport { fileViewsOf, type FileView } from \"./collect-output.js\";\n\n// Default-filename extensions for the content types the app uploads (mirrors\n// the SDK's map, which it does not export).\nconst EXT: Record<string, string> = {\n \"image/jpeg\": \".jpg\",\n \"image/png\": \".png\",\n \"audio/ogg\": \".ogg\",\n \"audio/wav\": \".wav\",\n \"audio/mp4\": \".m4a\",\n \"video/mp4\": \".mp4\",\n \"application/pdf\": \".pdf\",\n \"application/zip\": \".zip\",\n \"text/plain\": \".txt\",\n};\n\n/** The unique in-directory name for a file view: `<id>-<filename>` when the\n * uploader named it (basename'd — a filename is client-supplied wire data and\n * must not traverse), else `<id>` + a content-type extension. */\nexport function targetName(f: FileView): string {\n const id = f.id ?? \"file\";\n if (f.filename) return `${id}-${basename(f.filename)}`;\n return `${id}${EXT[f.contentType ?? \"\"] ?? \"\"}`;\n}\n\n/** Download every file the item carries into `dir`, stamping each view's\n * `path`. Never throws: per-file failures stamp `path: null` and come back as\n * warning strings. */\nexport async function saveItemFiles(item: object, dir: string): Promise<string[]> {\n const warnings: string[] = [];\n for (const f of fileViewsOf(item)) {\n try {\n f.path = await f.save!(join(dir, targetName(f)));\n } catch (err) {\n f.path = null;\n warnings.push(`could not save ${f.id ?? \"file\"}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n return warnings;\n}\n","// Parse the `sp collect --until` stop conditions. Group collects are bounded\n// by default (they end when every member finishes); the terminal-less modes\n// (--replies, --submissions) default to `forever` — they're watchers, and a\n// surprise 30s exit is worse than an explicit Ctrl-C. Any explicit --until\n// replaces the default.\n//\n// Grammar (repeatable / combinable; the first condition to trip wins):\n// --until complete all group members reached a terminal state (inputs)\n// --until idle:<dur> no event from any member for <dur>\n// --until count:<n> after <n> emitted events\n// --until timeout:<dur> after <dur> of wall-clock, regardless of activity\n// --until forever never stop (Ctrl-C / connection close only); sole condition\n//\n// <dur> is `<number><unit>` with unit ms|s|m|h (e.g. `500ms`, `30s`, `2m`).\n\nexport type UntilConfig = {\n complete: boolean;\n forever?: true;\n idleMs?: number;\n count?: number;\n timeoutMs?: number;\n};\n\nexport function parseDuration(s: string): number {\n const m = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h)$/.exec(s.trim());\n if (!m) throw new Error(`invalid duration \\`${s}\\`: use e.g. 500ms, 30s, 2m, 1h`);\n const n = Number(m[1]);\n const unit = m[2];\n const mult = unit === \"ms\" ? 1 : unit === \"s\" ? 1_000 : unit === \"m\" ? 60_000 : 3_600_000;\n return Math.round(n * mult);\n}\n\nexport function parseUntil(specs: readonly string[]): UntilConfig {\n const cfg: UntilConfig = { complete: false };\n for (const raw of specs) {\n const spec = raw.trim();\n if (spec === \"complete\") {\n cfg.complete = true;\n continue;\n }\n if (spec === \"forever\") {\n cfg.forever = true;\n continue;\n }\n const colon = spec.indexOf(\":\");\n if (colon === -1) {\n throw new Error(`invalid --until \\`${spec}\\`: expected complete | idle:<dur> | count:<n> | timeout:<dur> | forever`);\n }\n const key = spec.slice(0, colon);\n const val = spec.slice(colon + 1);\n switch (key) {\n case \"idle\":\n cfg.idleMs = parseDuration(val);\n break;\n case \"timeout\":\n cfg.timeoutMs = parseDuration(val);\n break;\n case \"count\": {\n const n = Number(val);\n if (!Number.isInteger(n) || n <= 0) throw new Error(`invalid --until count:\\`${val}\\`: expected a positive integer`);\n cfg.count = n;\n break;\n }\n default:\n throw new Error(`invalid --until \\`${spec}\\`: unknown condition \\`${key}\\` (expected idle|count|timeout|complete|forever)`);\n }\n }\n if (cfg.forever && (cfg.complete || cfg.idleMs !== undefined || cfg.count !== undefined || cfg.timeoutMs !== undefined)) {\n throw new Error(\"--until forever cannot be combined with other stop conditions\");\n }\n return cfg;\n}\n\n/** Fill in a sensible default when the caller passed no `--until`: inputs /\n * activity (which have a natural terminal) default to waiting for every member\n * to finish; replies / submissions (no terminal) run `forever` — they're\n * watchers, ended by Ctrl-C or an explicit `--until`. */\nexport function withDefaults(cfg: UntilConfig, mode: \"replies\" | \"inputs\" | \"submissions\" | \"activity\"): UntilConfig {\n if (cfg.forever) return cfg; // explicit opt-out of boundedness: no defaults\n const empty = !cfg.complete && cfg.idleMs === undefined && cfg.count === undefined && cfg.timeoutMs === undefined;\n if (!empty) return cfg;\n // inputs / activity have a natural terminal (every member completing); replies\n // and submissions don't — they watch until explicitly stopped.\n if (mode === \"inputs\" || mode === \"activity\") return { ...cfg, complete: true };\n return { ...cfg, forever: true };\n}\n","// `sp collect` — the agent-friendly collector. Gathers replies or inputs over a\n// task GROUP you sent (or submissions) off the ONE shared `ws/v1/events` stream,\n// and emits the unified NDJSON envelope (see collect-output.ts): a `sent` header,\n// one self-describing line per item (tagged with the recipient), and a terminal\n// `end` line stating WHY it stopped. Always bounded (never hangs).\n//\n// Group context comes from a prior send's `sent` line piped on stdin\n// (`sp task --format json | sp collect`) or from explicit `--group`/`--instance`\n// flags. Passing the send's `createdAt` (carried in the `sent` line) as the\n// resume point means nothing that arrived in the send→collect gap is missed.\n//\n// The stop conditions map onto Stream combinators: count/complete are\n// `takeUntilEffect` (recording WHY in a set-once Ref), wall-clock timeout is\n// `interruptWhen`, submission idle is `timeoutTo`, and group idle rides the\n// SDK's own idleMs (whose end the reason-resolution step names \"idle\").\n\nimport { mkdir } from \"node:fs/promises\";\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Duration, Effect, HashSet, Option, Ref, Schema, Stream } from \"effect\";\nimport type { Client, OrgClient, OrgMasterKey, Submission } from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n DEFAULT_BASE_URL,\n mappedText,\n passwordOption,\n quietOption,\n} from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { acquireClient, acquireOrgClient, sdkStream } from \"../services/sdk.js\";\nimport { AuthStore, bearerToken, VaultStore } from \"../services/stores.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { sharedWebSocketFactory } from \"../daemon/transport.js\";\nimport { resolveSince } from \"../since.js\";\nimport { saveItemFiles } from \"../save-files.js\";\nimport { parseUntil, withDefaults, type UntilConfig } from \"../until.js\";\nimport {\n formatSent,\n formatItem,\n formatSubmission,\n formatEnd,\n formatPrettyItem,\n formatPrettySubmission,\n type CollectedItem,\n type Member,\n type EndReason,\n type MemberStatus,\n} from \"../collect-output.js\";\n\n// The piped `sent` line from a prior `sp task --format json` (task members)\n// or `sp notify --format json` (notification members).\nconst SentLine = Schema.Struct({\n type: Schema.Literal(\"sent\"),\n groupId: Schema.optional(Schema.NullOr(Schema.String)),\n createdAt: Schema.optional(Schema.NullOr(Schema.String)),\n members: Schema.optional(\n Schema.Array(\n Schema.Struct({\n taskId: Schema.optional(Schema.String),\n notificationId: Schema.optional(Schema.String),\n // A subtask-append roster (`sp subtask --format json`): the member is\n // the PARENT instance, scoped to this sibling subtask.\n subtaskId: Schema.optional(Schema.String),\n recipient: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n publicId: Schema.String,\n name: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n }),\n ),\n ),\n});\ntype SentLine = typeof SentLine.Type;\n\nconst parseSentLine = Schema.decodeUnknownOption(Schema.parseJson(SentLine));\n\n/** Read the first `{\"type\":\"sent\",…}` line off stdin (when piped), returning as\n * soon as it's found — it never blocks waiting for the producer to close. */\nconst readSentLine: Effect.Effect<Option.Option<SentLine>> = Effect.suspend(() => {\n if (process.stdin.isTTY) return Effect.succeedNone;\n return Stream.fromAsyncIterable(process.stdin as AsyncIterable<Buffer>, (e) => e).pipe(\n Stream.map((chunk) => chunk.toString(\"utf8\")),\n Stream.splitLines,\n Stream.filterMap((line) => parseSentLine(line.trim())),\n Stream.runHead,\n Effect.orElseSucceed(() => Option.none<SentLine>()),\n Effect.ensuring(\n // Don't let an open stdin keep the process alive during collection.\n Effect.sync(() => {\n try { (process.stdin as unknown as { unref?: () => void }).unref?.(); } catch { /* noop */ }\n }),\n ),\n );\n});\n\ntype Mode = \"replies\" | \"inputs\" | \"submissions\" | \"activity\";\n\n// ---- credential resolution ---------------------------------------------\n\ntype Credential =\n | { kind: \"personal\"; apiToken: string; baseUrl: string }\n | { kind: \"org\"; bearer: string; baseUrl: string };\n\n/** Personal mode when `--api-token` (or $SP_API_TOKEN) is given;\n * otherwise fall back to the saved CLI session (org mode) — the same session\n * that powers org sends, so a logged-in admin collects without extra\n * credentials. Org mode follows the session's base URL; an explicitly\n * different `--base-url` is a conflict, not a silent override. */\nexport const resolveCredential = (\n tokenOpt: Option.Option<string>,\n baseUrlArg: string,\n): Effect.Effect<Credential, UserError, AuthStore> =>\n Effect.gen(function* () {\n if (Option.isSome(tokenOpt)) return { kind: \"personal\", apiToken: tokenOpt.value, baseUrl: baseUrlArg } as const;\n const store = yield* AuthStore;\n // An unreadable/corrupt auth.json behaves like \"not logged in\".\n const auth = yield* store.load.pipe(Effect.orElseSucceed(Option.none));\n if (Option.isNone(auth)) {\n return yield* Effect.fail(\n new UserError({\n message:\n \"no credential: pass --api-token (or set $SP_API_TOKEN) to collect your personal stream, or `sp auth login` to collect your organization's\",\n }),\n );\n }\n const session = auth.value;\n if (baseUrlArg !== DEFAULT_BASE_URL && baseUrlArg !== session.baseUrl) {\n return yield* Effect.fail(\n new UserError({\n message: `the CLI session is for ${session.baseUrl}, not ${baseUrlArg} — log in there, or pass --api-token to collect a personal stream instead`,\n }),\n );\n }\n return { kind: \"org\", bearer: bearerToken(session), baseUrl: session.baseUrl } as const;\n });\n\n/** Org master keys for decryption: the cached vault when present; a missing\n * cache prompts for the passphrase on a TTY (same UX as an encrypted org\n * send). Piped runs never prompt — stdin belongs to the `sent` line — and\n * proceed keyless with a warning (encrypted content passes through\n * undecrypted). `EncryptionDisabled` means keyless is simply correct. */\nexport const loadOrgMasterKeys = Effect.gen(function* () {\n const out = yield* CliOutput;\n const cached = yield* (yield* VaultStore).load.pipe(Effect.orElseSucceed(Option.none));\n const vault = Option.isSome(cached)\n ? cached.value\n : process.stdin.isTTY\n ? yield* (yield* VaultAccess).getOrPrompt.pipe(Effect.catchTag(\"EncryptionDisabled\", () => Effect.succeed(undefined)))\n : undefined;\n if (vault === undefined) {\n if (Option.isNone(cached) && !process.stdin.isTTY) {\n yield* out.warn(\"org vault is locked on this machine — encrypted org content will not decrypt (run `sp collect` once on a TTY to unlock)\");\n }\n return undefined;\n }\n const keys: OrgMasterKey[] = [vault.masterKeyCurrent, ...vault.masterKeyHistory].map((k) => ({ version: k.version, key: k.key }));\n return keys;\n});\n\nexport const collectCommand = Command.make(\n \"collect\",\n {\n group: Options.text(\"group\").pipe(\n Options.withDescription(\"Group id (grptsk_…) to collect over. Usually supplied via the piped `sent` line instead.\"),\n Options.optional,\n ),\n instance: Options.text(\"instance\").pipe(\n Options.withDescription(\"Member instance id to collect: a task (tsk_…), a notification (ntf_…), or a subtask-scoped pair (tsk_…/sub_…). Repeatable. Augments/overrides the piped `sent` line's members.\"),\n Options.repeated,\n ),\n replies: Options.boolean(\"replies\").pipe(Options.withDescription(\"Collect only replies. Default (no mode flag) is the full activity stream: inputs, replies, and completions.\")),\n inputs: Options.boolean(\"inputs\").pipe(Options.withDescription(\"Collect only input events (waits for every member to complete by default).\")),\n submissions: Options.boolean(\"submissions\").pipe(Options.withDescription(\"Collect submissions (your inbox) instead of a group's events.\")),\n since: mappedText(\"since\", resolveSince).pipe(\n Options.withDescription(\"Resume point (`24h`, `7d`, or ISO 8601). Backfills group events or submissions from that point; defaults to the send's createdAt from the piped `sent` line. Implies --direct (the broker can't serve a deep backfill).\"),\n Options.optional,\n ),\n until: Options.text(\"until\").pipe(\n Options.withDescription(\"Stop condition. Repeatable: complete | idle:<dur> | count:<n> | timeout:<dur> | forever (never stop; Ctrl-C to end). Default: complete for group collects; --replies / --submissions watch forever.\"),\n Options.repeated,\n ),\n format: Options.choice(\"format\", [\"json\", \"pretty\"] as const).pipe(\n Options.withDescription(\"Output format: json (NDJSON, agent contract) or pretty (human).\"),\n Options.withDefault(\"json\"),\n ),\n direct: Options.boolean(\"direct\").pipe(\n Options.withDescription(\"Open an independent WS connection. By default sp shares ONE broker connection across all processes (auto-started); --direct bypasses it.\"),\n ),\n \"save-files\": Options.text(\"save-files\").pipe(\n Options.withDescription(\"Download every collected file (photo/voice/file uploads, reply and submission files) into this directory as it streams in, decrypted and checksum-verified. Each file object on the emitted line gains `path`: the saved location, or null when its download failed.\"),\n Options.optional,\n ),\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const cred = yield* resolveCredential(args[\"api-token\"], args[\"base-url\"]);\n const baseUrl = cred.baseUrl;\n const sinceIso = Option.getOrUndefined(args.since);\n\n const mode: Mode =\n args.submissions ? \"submissions\"\n : args.inputs && args.replies ? \"activity\" // both → the combined inputs+replies stream\n : args.inputs ? \"inputs\"\n : args.replies ? \"replies\"\n : \"activity\"; // no mode flag → everything the group produces (inputs, replies, completions)\n\n const until = withDefaults(\n yield* Effect.try({\n try: () => parseUntil(args.until),\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n }),\n mode,\n );\n\n // Default: route the event stream through the local broker (ONE shared WS\n // for all sp processes on the same credential — personal AND org alike);\n // `--direct` opts out. An explicit `--since` also goes direct: the broker\n // only holds a recent window (its ring), so it can't serve a deep\n // backfill — same rule as `sp events`. The broker is best-effort —\n // sharedWebSocketFactory falls back to a direct connection on any failure.\n const webSocketFactory = args.direct || sinceIso !== undefined\n ? Option.none()\n : yield* sharedWebSocketFactory({\n credential:\n cred.kind === \"personal\"\n ? { kind: \"personal\", apiToken: cred.apiToken }\n : { kind: \"org\", bearer: cred.bearer },\n baseUrl,\n });\n const factoryConfig = Option.match(webSocketFactory, { onNone: () => ({}), onSome: (f) => ({ webSocketFactory: f }) });\n\n const orgKeys = cred.kind === \"org\" ? yield* loadOrgMasterKeys : undefined;\n if (cred.kind === \"org\") yield* out.info(`collecting via org session (${baseUrl})`);\n\n const saveDir = Option.getOrUndefined(args[\"save-files\"]);\n if (saveDir !== undefined) {\n yield* Effect.tryPromise({\n try: () => mkdir(saveDir, { recursive: true }),\n catch: (e) => new UserError({ message: `cannot create --save-files directory ${saveDir}: ${e instanceof Error ? e.message : String(e)}` }),\n });\n }\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client =\n cred.kind === \"personal\"\n ? yield* acquireClient({\n baseUrl,\n apiToken: cred.apiToken,\n passwords: [...args.password],\n ...factoryConfig,\n })\n : yield* acquireOrgClient({\n baseUrl,\n bearerToken: cred.bearer,\n ...(orgKeys !== undefined ? { orgMasterKeys: orgKeys } : {}),\n ...factoryConfig,\n });\n\n if (mode === \"submissions\") return yield* collectSubmissions(client, args.format, until, sinceIso, saveDir);\n\n // Group modes: assemble the member roster from the piped `sent` line\n // and/or explicit --instance ids.\n const sent = Option.getOrUndefined(yield* readSentLine);\n const groupId = Option.getOrUndefined(args.group) ?? sent?.groupId ?? undefined;\n const createdAt = sinceIso ?? sent?.createdAt ?? undefined;\n\n const members: Member[] = [];\n const seen = new Set<string>();\n const addMember = (id: string | undefined, recipient: Member[\"recipient\"], subtaskId?: string) => {\n const key = subtaskId !== undefined ? `${id}/${subtaskId}` : id;\n if (id && key && !seen.has(key)) {\n seen.add(key);\n members.push({\n id,\n kind: id.startsWith(\"ntf_\") ? \"notification\" : \"task\",\n recipient,\n ...(subtaskId !== undefined ? { subtaskId } : {}),\n });\n }\n };\n for (const m of sent?.members ?? []) {\n addMember(\n m.taskId ?? m.notificationId,\n m.recipient ? { publicId: m.recipient.publicId, name: m.recipient.name ?? null } : null,\n m.subtaskId,\n );\n }\n for (const spec of args.instance) {\n const [id, subtaskId] = spec.split(\"/\", 2) as [string, string?];\n if (id.startsWith(\"sub_\")) {\n return yield* Effect.fail(\n new UserError({\n message: `a subtask cannot be collected by its id alone (events route under the parent task) — pass --instance <parent tsk_…>/${id}`,\n }),\n );\n }\n addMember(id, null, subtaskId);\n }\n\n if (members.length === 0) {\n return yield* Effect.fail(\n new UserError({\n message: \"no members to collect: pipe a send's `sent` line (`sp task --format json | sp collect`) or pass --instance <tsk_…|ntf_…|tsk_…/sub_…> (repeatable)\",\n }),\n );\n }\n const notifRoster = members[0]!.kind === \"notification\";\n if (members.some((m) => (m.kind === \"notification\") !== notifRoster)) {\n return yield* Effect.fail(\n new UserError({ message: \"cannot mix task (tsk_…) and notification (ntf_…) members in one collect — run one per kind\" }),\n );\n }\n // A subtask roster comes from a piped `sp subtask --format json` line\n // or explicit `--instance tsk_…/sub_…` pairs: the members are parent\n // instances scoped to one sibling each.\n const subRoster = members[0]!.subtaskId !== undefined;\n if (members.some((m) => (m.subtaskId !== undefined) !== subRoster)) {\n return yield* Effect.fail(\n new UserError({\n message: \"cannot mix subtask-scoped members (a piped `sp subtask --format json` line or --instance tsk_…/sub_…) with plain task/notification members in one collect\",\n }),\n );\n }\n\n // Group idle rides the SDK's own idleMs (its group streams end\n // cleanly on idle); collectGroup's reason resolution names that end\n // \"idle\".\n const streamOpts = { replay: true, ...(until.idleMs !== undefined ? { idleMs: until.idleMs } : {}) };\n const watchGroupId = groupId ?? members[0]!.id; // synthetic id for a single-instance collect\n\n // One stream per roster kind + mode; all yield `{instance, item,\n // recipient}`, so a single tap + `formatItem` (keyed on item.kind)\n // serves replies, inputs, activity, and notification answers\n // uniformly.\n let source: (signal: AbortSignal) => AsyncIterableIterator<CollectedItem>;\n if (notifRoster) {\n // Notifications carry no reply composer; their whole activity IS\n // the single answer, so every non-replies mode maps to `inputs()`.\n if (mode === \"replies\") {\n return yield* Effect.fail(\n new UserError({ message: \"notifications have no replies — collect their answers with --inputs or no mode flag\" }),\n );\n }\n const group = client.watchNotificationGroup({\n groupId: watchGroupId,\n ...(createdAt ? { createdAt } : {}),\n members: members.map((m) => (m.recipient ? { notificationId: m.id, recipient: m.recipient } : { notificationId: m.id })),\n });\n source = (signal) => group.inputs({ ...streamOpts, signal });\n } else if (subRoster) {\n // Sibling-scoped streams: the root task streams DROP subtask-scoped\n // events (streamEntity's scope filter), so a subtask collect must\n // watch each sibling directly.\n const group = client.watchSubtaskGroup({\n groupId: watchGroupId,\n ...(createdAt ? { createdAt } : {}),\n members: members.map((m) => ({\n taskId: m.id,\n subtaskId: m.subtaskId!,\n ...(m.recipient ? { recipient: m.recipient } : {}),\n })),\n });\n source = (signal) =>\n mode === \"inputs\" ? group.inputs({ ...streamOpts, signal })\n : mode === \"replies\" ? group.replies({ ...streamOpts, signal })\n : group.activity({ ...streamOpts, signal });\n } else {\n const group = client.watchTaskGroup({\n groupId: watchGroupId,\n ...(createdAt ? { createdAt } : {}),\n members: members.map((m) => (m.recipient ? { taskId: m.id, recipient: m.recipient } : { taskId: m.id })),\n });\n source = (signal) =>\n mode === \"inputs\" ? group.inputs({ ...streamOpts, signal })\n : mode === \"replies\" ? group.replies({ ...streamOpts, signal })\n : group.activity({ ...streamOpts, signal });\n }\n\n if (args.format === \"json\") yield* out.print(formatSent(groupId, createdAt, members));\n else yield* out.info(`collecting ${mode} over ${members.length} member(s)${groupId ? ` of ${groupId}` : \"\"}`);\n\n yield* collectGroup(watchGroupId, source, members, args.format, until, saveDir);\n }),\n );\n }),\n);\n\n/** A set-once end-reason cell: the first condition to trip names the reason. */\nconst makeReason = Effect.map(Ref.make(Option.none<EndReason>()), (ref) => ({\n set: (r: EndReason) => Ref.update(ref, Option.orElse(() => Option.some(r))),\n get: Ref.get(ref),\n}));\n\n/** Download the item's files into `dir` (stamping each view's `path`) before\n * its line is emitted; failures warn on stderr and stamp `path: null`. */\nconst saveFiles = (item: Parameters<typeof saveItemFiles>[0], dir: string | undefined) =>\n dir === undefined\n ? Effect.void\n : Effect.gen(function* () {\n const out = yield* CliOutput;\n const warnings = yield* Effect.promise(() => saveItemFiles(item, dir));\n for (const w of warnings) yield* out.warn(w);\n });\n\nconst collectSubmissions = (client: Client | OrgClient, format: \"json\" | \"pretty\", until: UntilConfig, since?: string, saveDir?: string) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const reason = yield* makeReason;\n const counts = yield* Ref.make(0);\n\n // Idle/timeout are enforced HERE (not delegated to the SDK's idle option):\n // ending the stream interrupts the scope, whose finalizer aborts the SDK\n // generator via its signal — that breaks the reconnect backoff, so\n // termination is prompt even on a flapping connection.\n const failed = yield* sdkStream(\"submissions stream\", (signal) =>\n client.submissions({ signal, ...(since !== undefined ? { since } : {}) }),\n ).pipe(\n until.idleMs !== undefined\n ? Stream.timeoutTo(Duration.millis(until.idleMs), Stream.drain(Stream.fromEffect(reason.set(\"idle\"))))\n : (s) => s,\n Stream.tap((s: Submission) =>\n saveFiles(s, saveDir).pipe(\n Effect.zipRight(out.print(format === \"json\" ? formatSubmission(s) : formatPrettySubmission(s))),\n Effect.zipRight(Ref.update(counts, (n) => n + 1)),\n ),\n ),\n until.count !== undefined\n ? Stream.takeUntilEffect(() =>\n Effect.gen(function* () {\n if ((yield* Ref.get(counts)) < until.count!) return false;\n yield* reason.set(\"count\");\n return true;\n }),\n )\n : (s) => s,\n until.timeoutMs !== undefined\n ? Stream.interruptWhen(Effect.sleep(Duration.millis(until.timeoutMs)).pipe(Effect.zipRight(reason.set(\"timeout\"))))\n : (s) => s,\n Stream.runDrain,\n Effect.matchEffect({\n onFailure: (e) =>\n reason.set(\"error\").pipe(\n Effect.zipRight(out.error(`submissions stream failed: ${e.cause instanceof Error ? e.cause.message : String(e.cause)}`)),\n Effect.as(true),\n ),\n onSuccess: () => Effect.succeed(false),\n }),\n );\n\n const total = yield* Ref.get(counts);\n const why = Option.getOrElse(yield* reason.get, (): EndReason => \"closed\");\n if (format === \"json\") {\n yield* out.print(formatEnd(why, total > 0 ? { submission: total } : {}, undefined, why === \"error\" ? \"submissions stream failed\" : undefined));\n } else {\n yield* out.info(`done (${why}): ${JSON.stringify(total > 0 ? { submission: total } : {})}`);\n }\n if (failed) return yield* Effect.fail(new Aborted());\n });\n\nconst collectGroup = (\n groupId: string,\n source: (signal: AbortSignal) => AsyncIterableIterator<CollectedItem>,\n members: readonly Member[],\n format: \"json\" | \"pretty\",\n until: UntilConfig,\n saveDir?: string,\n) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const reason = yield* makeReason;\n const counts = yield* Ref.make<Record<string, number>>({});\n const completed = yield* Ref.make(HashSet.empty<string>());\n const deleted = yield* Ref.make(HashSet.empty<string>());\n const canceled = yield* Ref.make(HashSet.empty<string>());\n const declined = yield* Ref.make(HashSet.empty<string>());\n const expired = yield* Ref.make(HashSet.empty<string>());\n const total = yield* Ref.make(0);\n\n // A member is DONE once its instance is terminal: answered, deleted,\n // sender-canceled, or declined by every recipient — else a\n // cancel-the-rest (or a decline) would hang `--until complete`.\n const doneCount = Effect.gen(function* () {\n return HashSet.size(yield* Ref.get(completed)) + HashSet.size(yield* Ref.get(deleted))\n + HashSet.size(yield* Ref.get(canceled)) + HashSet.size(yield* Ref.get(declined))\n + HashSet.size(yield* Ref.get(expired));\n });\n\n const instanceIdOf = (g: CollectedItem): string => {\n const inst = g.instance as { taskId?: string; notificationId?: string; subtaskId?: string; parentTaskId?: string };\n if (inst.subtaskId !== undefined) return `${inst.parentTaskId}/${inst.subtaskId}`;\n return inst.taskId ?? inst.notificationId ?? \"\";\n };\n // On a subtask roster the member IS the sibling, so its scoped terminals\n // (completed / canceled / declined) are member-terminal — unlike a task\n // roster, where a subtask cancel leaves the chain member live.\n const subScoped = (g: CollectedItem): boolean => (g.instance as { subtaskId?: string }).subtaskId !== undefined;\n\n const failed = yield* sdkStream(\"collect stream\", source).pipe(\n Stream.tap((g: CollectedItem) =>\n Effect.gen(function* () {\n yield* saveFiles(g.item, saveDir);\n yield* out.print(format === \"json\" ? formatItem(g, groupId) : formatPrettyItem(g));\n yield* Ref.update(total, (n) => n + 1);\n const kind = g.item.kind;\n const label =\n kind === \"reply\" ? \"reply\"\n : kind === \"input\" ? \"input\"\n : kind === \"taskDeleted\" ? \"deleted\"\n : kind === \"taskCanceled\" || kind === \"subtaskCanceled\" ? \"canceled\"\n : kind === \"taskDeclinedByRecipient\" || kind === \"subtaskDeclinedByRecipient\" ? \"declined\"\n : kind === \"taskDeclined\" || kind === \"subtaskDeclined\" ? \"all-declined\"\n : kind === \"taskExpired\" ? \"expired\"\n : \"completed\";\n yield* Ref.update(counts, (c) => ({ ...c, [label]: (c[label] ?? 0) + 1 }));\n if (kind === \"taskCompleted\" || kind === \"notificationCompleted\" || kind === \"subtaskCompleted\") yield* Ref.update(completed, HashSet.add(instanceIdOf(g)));\n if (kind === \"taskDeleted\") yield* Ref.update(deleted, HashSet.add(instanceIdOf(g)));\n // Only a ROOT cancel is terminal for a task member; a subtaskCanceled\n // leaves the chain live — except on a subtask roster, where the\n // canceled sibling IS the member.\n if (kind === \"taskCanceled\" || (kind === \"subtaskCanceled\" && subScoped(g))) yield* Ref.update(canceled, HashSet.add(instanceIdOf(g)));\n // Likewise only the COLLECTIVE decline is terminal; a per-recipient\n // `taskDeclinedByRecipient` is a countdown signal (shared mode).\n if (kind === \"taskDeclined\" || (kind === \"subtaskDeclined\" && subScoped(g))) yield* Ref.update(declined, HashSet.add(instanceIdOf(g)));\n // The deadline passed unanswered — terminal for the member, like a\n // root cancel.\n if (kind === \"taskExpired\") yield* Ref.update(expired, HashSet.add(instanceIdOf(g)));\n }),\n ),\n until.count !== undefined\n ? Stream.takeUntilEffect(() =>\n Effect.gen(function* () {\n if ((yield* Ref.get(total)) < until.count!) return false;\n yield* reason.set(\"count\");\n return true;\n }),\n )\n : (s) => s,\n // activity() has no natural terminal, so enforce \"every member finished\"\n // here (a no-op for replies, redundant-but-harmless for inputs).\n until.complete && members.length > 0\n ? Stream.takeUntilEffect(() =>\n Effect.gen(function* () {\n if ((yield* doneCount) < members.length) return false;\n yield* reason.set(\"complete\");\n return true;\n }),\n )\n : (s) => s,\n until.timeoutMs !== undefined\n ? Stream.interruptWhen(Effect.sleep(Duration.millis(until.timeoutMs)).pipe(Effect.zipRight(reason.set(\"timeout\"))))\n : (s) => s,\n Stream.runDrain,\n Effect.matchEffect({\n onFailure: (e) =>\n reason.set(\"error\").pipe(\n Effect.zipRight(out.error(`collect stream failed: ${e.cause instanceof Error ? e.cause.message : String(e.cause)}`)),\n Effect.as(true),\n ),\n onSuccess: () => Effect.succeed(false),\n }),\n );\n\n // No explicit trip → the stream ended on its own: everyone finished, the\n // SDK idle window elapsed, or the connection closed.\n const done = yield* doneCount;\n const fallback: EndReason =\n members.length > 0 && done >= members.length ? \"complete\" : until.idleMs !== undefined ? \"idle\" : \"closed\";\n const why = Option.getOrElse(yield* reason.get, () => fallback);\n\n const memberStatus: MemberStatus = {\n total: members.length,\n completed: HashSet.size(yield* Ref.get(completed)),\n deleted: HashSet.size(yield* Ref.get(deleted)),\n canceled: HashSet.size(yield* Ref.get(canceled)),\n declined: HashSet.size(yield* Ref.get(declined)),\n expired: HashSet.size(yield* Ref.get(expired)),\n pending: members.length - done,\n };\n if (format === \"json\") {\n yield* out.print(formatEnd(why, yield* Ref.get(counts), memberStatus, why === \"error\" ? \"collect stream failed\" : undefined));\n } else {\n yield* out.info(`done (${why}): ${JSON.stringify(yield* Ref.get(counts))}`);\n }\n if (failed) return yield* Effect.fail(new Aborted());\n });\n","// `sp cancel <id>` — sender-side withdrawal of a pending task, follow-up, or\n// whole task group. The id's prefix picks the endpoint: `tsk_…` cancels one\n// task, `sub_…` one subtask (the chain stays live), `grptsk_…` every\n// still-pending instance of a group (cancel-the-rest; completed members are\n// skipped and counted).\n//\n// Credential mirrors `sp collect`: `--api-token` (or $SP_API_TOKEN) cancels a\n// personal send; otherwise the saved CLI session cancels the organization's —\n// the same session that sent the org task in the first place.\n//\n// `--note` encryption carries the note's OWN marker (a cancel is authored\n// after the send, so an org note may use a newer master_key version than the\n// task's marker names):\n// - org path: encrypted under the current vault master key BY DEFAULT when\n// the org has encryption enabled (like every other org send); --no-encrypt\n// opts out.\n// - personal path: ONE `--password` names the chain's key — `pw@topic`\n// seals under that topic's key, a bare `pw` under the ACCOUNT DEFAULT key\n// (server password_salt, for canceling a topicless self-send). Without\n// one, the note ships plaintext, with a warning.\n\nimport { Args, Command, Options } from \"@effect/cli\";\nimport { Effect, Option } from \"effect\";\nimport {\n cancelSubtask,\n cancelTask,\n cancelTaskGroup,\n deriveKey,\n encrypt,\n fetchUserInfo,\n type CancelReason,\n type CancelRequestBody,\n} from \"@simplepush/sdk\";\n\nimport { apiTokenOption, baseUrlOption, passwordOption, quietOption, type PasswordFlag } from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { sdkCall } from \"../services/sdk.js\";\nimport { resolveCredential } from \"./collect.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\n\nconst idArg = Args.text({ name: \"id\" }).pipe(\n Args.withDescription(\"What to cancel: a task (`tsk_…`), a subtask (`sub_…`), or a task group (`grptsk_…`).\"),\n);\n\nconst reasonOption = Options.choice(\"reason\", [\"canceled\", \"answered\", \"superseded\"] as const).pipe(\n Options.withDescription(\n \"Why: plain withdrawal (default), another recipient's answer made the rest moot (`answered`), or a replacement exists (`superseded`).\",\n ),\n Options.withDefault(\"canceled\" as CancelReason),\n);\n\nconst noteOption = Options.text(\"note\").pipe(\n Options.withDescription(\n \"Free-text explanation shown on the recipients' canceled card. Encrypted under the org master key on the org path (default when enabled); on the personal path seal it with --password pw@topic (topic send) or a bare --password (self-send, account default key).\",\n ),\n Options.optional,\n);\n\nconst supersededByOption = Options.text(\"superseded-by\").pipe(\n Options.withDescription(\n \"The replacement's id — a task id, a subtask id of the same chain, or (for a group cancel) the replacement group id. Requires --reason superseded.\",\n ),\n Options.optional,\n);\n\nconst noEncryptOption = Options.boolean(\"no-encrypt\").pipe(\n Options.withDescription(\"For org cancels: send the note in plaintext even when the org vault is unlocked.\"),\n);\n\nexport const cancelCommand = Command.make(\n \"cancel\",\n {\n id: idArg,\n reason: reasonOption,\n note: noteOption,\n \"superseded-by\": supersededByOption,\n password: passwordOption,\n \"no-encrypt\": noEncryptOption,\n \"api-token\": apiTokenOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const note = Option.getOrUndefined(args.note);\n const supersededBy = Option.getOrUndefined(args[\"superseded-by\"]);\n if (supersededBy !== undefined && args.reason !== \"superseded\") {\n return yield* Effect.fail(new UserError({ message: \"--superseded-by requires --reason superseded\" }));\n }\n\n const credential = yield* resolveCredential(args[\"api-token\"], args[\"base-url\"]);\n\n // Seal the note under the chain's key where one is available, stamping\n // the note's own marker; otherwise ship plaintext LOUDLY.\n let noteFields: Pick<CancelRequestBody, \"note\" | \"encryption\"> = {};\n if (note !== undefined) {\n if (credential.kind === \"org\") {\n const access = yield* VaultAccess;\n const vault = yield* access.forSendOrPlaintext(args[\"no-encrypt\"]);\n if (vault) {\n const sealed = yield* sdkCall(\"encrypt note\", () => encrypt(vault.masterKeyCurrent.key, note));\n noteFields = { note: sealed, encryption: { type: \"org\", v: vault.masterKeyCurrent.version } };\n } else {\n noteFields = { note };\n yield* out.warn(\"note sent unencrypted (org encryption disabled or --no-encrypt)\");\n }\n } else {\n // Personal path: ONE --password names the chain's key. A `pw@topic`\n // pair seals under that topic's key (the pair already names the\n // topic — no separate flag); a bare `pw` seals under the ACCOUNT\n // DEFAULT key (server password_salt), exactly how a topicless\n // self-send encrypted in the first place.\n const passwords = args.password as ReadonlyArray<PasswordFlag>;\n if (passwords.length > 1) {\n return yield* Effect.fail(\n new UserError({ message: \"pass exactly one --password so the note's key is unambiguous\" }),\n );\n }\n const flag = passwords[0];\n if (flag === undefined) {\n noteFields = { note };\n yield* out.warn(\n \"note sent unencrypted — seal it with --password <pw>@<topic> (topic send) or a bare --password (self-send)\",\n );\n } else if (Array.isArray(flag)) {\n const derived = yield* sdkCall(\"derive note key\", () => deriveKey(flag[0], flag[1]));\n const sealed = yield* sdkCall(\"encrypt note\", () => encrypt(derived.symmetricKey, note));\n noteFields = { note: sealed, encryption: { type: \"personal\", keyFingerprint: derived.fingerprint } };\n } else {\n const info = yield* sdkCall(\"fetch password salt\", () =>\n fetchUserInfo({ baseUrl: new URL(credential.baseUrl), apiToken: credential.apiToken, fetch }),\n );\n const derived = yield* sdkCall(\"derive note key\", () => deriveKey(flag, info.passwordSalt));\n const sealed = yield* sdkCall(\"encrypt note\", () => encrypt(derived.symmetricKey, note));\n noteFields = { note: sealed, encryption: { type: \"personal\", keyFingerprint: derived.fingerprint } };\n }\n }\n }\n\n const body: CancelRequestBody = {\n reason: args.reason,\n ...noteFields,\n ...(supersededBy !== undefined ? { supersededBy } : {}),\n };\n const baseUrl = new URL(credential.baseUrl);\n const authHeaders: Record<string, string> =\n credential.kind === \"personal\"\n ? { \"API-Token\": credential.apiToken }\n : { Authorization: `Bearer ${credential.bearer}` };\n\n if (args.id.startsWith(\"grptsk_\")) {\n const result = yield* sdkCall(\"cancel task group\", () => cancelTaskGroup({ baseUrl, groupId: args.id, body, authHeaders }));\n // Machine-readable counts on stdout: how many instances were still\n // open (canceled) vs already terminal (skipped).\n yield* out.print(JSON.stringify({ type: \"canceled\", groupId: args.id, ...result }));\n } else if (args.id.startsWith(\"tsk_\")) {\n yield* sdkCall(\"cancel task\", () => cancelTask({ baseUrl, taskId: args.id, body, authHeaders }));\n yield* out.info(`canceled ${args.id}`);\n } else if (args.id.startsWith(\"sub_\")) {\n yield* sdkCall(\"cancel subtask\", () => cancelSubtask({ baseUrl, subtaskId: args.id, body, authHeaders }));\n yield* out.info(`canceled ${args.id}`);\n } else {\n return yield* Effect.fail(\n new UserError({ message: `cannot cancel \\`${args.id}\\`: expected a tsk_…, sub_…, or grptsk_… id` }),\n );\n }\n }),\n).pipe(Command.withDescription(\"Cancel a pending task, subtask, or task group you sent (sender-side withdrawal).\"));\n","// `sp daemon` — run the shared events broker in the foreground. Normally started\n// automatically by `sp collect` / `sp events` (detached), but exposed so it can\n// be run/inspected directly. Blocks until the upstream stream ends or it\n// idle-exits once the last client leaves.\n//\n// Credential precedence mirrors `sp collect`: an explicit --api-token (or\n// $SP_API_TOKEN) runs a PERSONAL broker; otherwise\n// $SP_DAEMON_BEARER (set by the auto-spawn, so the child's socket path\n// deterministically matches the parent's probe) or the saved CLI session runs\n// an ORG broker.\n\nimport { Command } from \"@effect/cli\";\nimport { Effect, Option } from \"effect\";\n\nimport { apiTokenOption, baseUrlOption, quietOption } from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { AuthStore, bearerToken } from \"../services/stores.js\";\nimport { runDaemon } from \"../daemon/server.js\";\nimport type { BrokerCredential } from \"../daemon/paths.js\";\n\nconst resolveDaemonCredential = (\n tokenOpt: Option.Option<string>,\n): Effect.Effect<BrokerCredential, UserError, AuthStore> =>\n Effect.gen(function* () {\n if (Option.isSome(tokenOpt)) return { kind: \"personal\", apiToken: tokenOpt.value } as const;\n const spawnedBearer = process.env.SP_DAEMON_BEARER;\n if (spawnedBearer) return { kind: \"org\", bearer: spawnedBearer } as const;\n const store = yield* AuthStore;\n const auth = yield* store.load.pipe(Effect.orElseSucceed(Option.none));\n if (Option.isSome(auth)) return { kind: \"org\", bearer: bearerToken(auth.value) } as const;\n return yield* Effect.fail(\n new UserError({\n message:\n \"no credential: pass --api-token (or set $SP_API_TOKEN) for a personal broker, or `sp auth login` for an org broker\",\n }),\n );\n });\n\nexport const daemonCommand = Command.make(\n \"daemon\",\n {\n \"api-token\": apiTokenOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const credential = yield* resolveDaemonCredential(args[\"api-token\"]);\n yield* runDaemon({ credential, baseUrl: args[\"base-url\"] });\n }),\n);\n","// `sp download` — fetch one file a collect line referenced: a task input\n// upload (inp_…), a reply file (rfl_…), or a submission file (sbf_…), by the\n// ids the NDJSON envelope carries.\n//\n// Two requests, no event replay: the download-url endpoints return the stored\n// file's own description (filename, contentType, size, checksum of the STORED\n// blob, and the encryption marker naming its decrypting key) alongside the\n// presigned GET. Fetch, verify the checksum, decrypt when a marker is present,\n// save. Scopes map 1:1 onto the backend routes — a subtask file is addressed\n// by its own sub_… id.\n\nimport { createHash } from \"node:crypto\";\nimport { mkdir, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { Args, Command, Options } from \"@effect/cli\";\nimport { Effect, Option } from \"effect\";\nimport { Keyring, decryptBytes, deriveKey, fetchUserInfo } from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n type PasswordFlag,\n} from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { loadOrgMasterKeys, resolveCredential } from \"./collect.js\";\n\n// One shared line for every id mixup — sub_/sbm_ are the classic confusion.\nconst ID_GLOSSARY = \"(ids: tsk_ task, sub_ subtask, sbm_ submission; files: inp_ input upload, rfl_ reply file, sbf_ submission file)\";\n\ntype Scope = \"task\" | \"subtask\" | \"submission\";\n\nconst resolveScope = (scopeId: string, fileId: string): Effect.Effect<Scope, UserError> => {\n const scope: Scope | undefined = scopeId.startsWith(\"tsk_\")\n ? \"task\"\n : scopeId.startsWith(\"sub_\")\n ? \"subtask\"\n : scopeId.startsWith(\"sbm_\")\n ? \"submission\"\n : undefined;\n if (scope === undefined) {\n return Effect.fail(new UserError({ message: `expected a task (tsk_…), subtask (sub_…), or submission (sbm_…) scope id, got '${scopeId}' ${ID_GLOSSARY}` }));\n }\n const wantsChain = fileId.startsWith(\"inp_\") || fileId.startsWith(\"rfl_\");\n const wantsSubmission = fileId.startsWith(\"sbf_\");\n if (!wantsChain && !wantsSubmission) {\n return Effect.fail(new UserError({ message: `expected an input upload (inp_…), reply file (rfl_…), or submission file (sbf_…) id, got '${fileId}' ${ID_GLOSSARY}` }));\n }\n if (wantsSubmission !== (scope === \"submission\")) {\n return Effect.fail(\n new UserError({\n message: wantsSubmission\n ? `${fileId} is a submission file — pass its sbm_… id, not ${scopeId} ${ID_GLOSSARY}`\n : `${fileId} lives on a task chain — pass the tsk_… or sub_… id it belongs to, not ${scopeId} ${ID_GLOSSARY}`,\n }),\n );\n }\n return Effect.succeed(scope);\n};\n\nconst downloadUrlPath = (scope: Scope, scopeId: string, fileId: string): string => {\n if (scope === \"submission\") return `/v1/submissions/${scopeId}/files/${fileId}/download-url`;\n const kind = fileId.startsWith(\"inp_\") ? \"inputs\" : \"replies\";\n const base = scope === \"task\" ? \"tasks\" : \"subtasks\";\n return `/v1/${base}/${scopeId}/${kind}/${fileId}/download-url`;\n};\n\ntype DownloadUrlResponse = {\n presignedGetUrl: string;\n expiresAt: string;\n filename?: string;\n contentType?: string;\n size?: number;\n durationSeconds?: number;\n checksumSha256?: string;\n encryption?: { type: string; keyFingerprint?: string; v?: number };\n};\n\n/** Resolve where to write: an explicit file path, an existing directory (the\n * server-declared filename inside it), or the current directory. */\nconst resolveTarget = async (outArg: string | undefined, filename: string): Promise<string> => {\n if (outArg === undefined) return path.join(process.cwd(), filename);\n try {\n const s = await stat(outArg);\n if (s.isDirectory()) return path.join(outArg, filename);\n } catch {\n // Not existing: treat as a file path; ensure the parent exists.\n await mkdir(path.dirname(outArg), { recursive: true });\n }\n return outArg;\n};\n\nexport const downloadCommand = Command.make(\n \"download\",\n {\n scopeId: Args.text({ name: \"scope-id\" }).pipe(\n Args.withDescription(\"The containing entity: a task (tsk_…), a subtask (sub_…), or a submission (sbm_…) — the id the collect line carries.\"),\n ),\n fileId: Args.text({ name: \"file-id\" }).pipe(\n Args.withDescription(\"The file to download: an input upload (inp_…), reply file (rfl_…), or submission file (sbf_…) — the `id` on the line's file object.\"),\n ),\n out: Options.text(\"out\").pipe(\n Options.withDescription(\"Where to save: a file path, an existing directory (the upload's filename is used inside it), or omitted for the current directory.\"),\n Options.optional,\n ),\n format: Options.choice(\"format\", [\"json\", \"pretty\"] as const).pipe(\n Options.withDescription(\"Output format: json (one `downloaded` line) or pretty (the saved path).\"),\n Options.withDefault(\"json\"),\n ),\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const scope = yield* resolveScope(args.scopeId, args.fileId);\n const cred = yield* resolveCredential(args[\"api-token\"], args[\"base-url\"]);\n const orgKeys = cred.kind === \"org\" ? yield* loadOrgMasterKeys : undefined;\n const baseUrl = cred.baseUrl.replace(/\\/+$/, \"\");\n const authHeaders: Record<string, string> =\n cred.kind === \"personal\" ? { \"API-Token\": cred.apiToken } : { Authorization: `Bearer ${cred.bearer}` };\n\n // Request 1: presigned URL + the stored blob's own description.\n const meta = yield* Effect.tryPromise({\n try: async (): Promise<DownloadUrlResponse> => {\n const res = await fetch(`${baseUrl}${downloadUrlPath(scope, args.scopeId, args.fileId)}`, {\n method: \"POST\",\n headers: authHeaders,\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n let msg = `download-url failed (${res.status})`;\n try {\n const parsed = JSON.parse(body) as { msg?: string };\n if (parsed.msg) msg = `${msg}: ${parsed.msg}`;\n } catch {\n if (body) msg = `${msg}: ${body.slice(0, 200)}`;\n }\n throw new Error(msg);\n }\n return (await res.json()) as DownloadUrlResponse;\n },\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n\n // Request 2: the blob itself (the presigned URL is self-authenticating).\n const stored = yield* Effect.tryPromise({\n try: async () => {\n const res = await fetch(meta.presignedGetUrl);\n if (!res.ok) throw new Error(`fetching the file failed (${res.status})`);\n return new Uint8Array(await res.arrayBuffer());\n },\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n\n // Integrity: the declared checksum describes the STORED blob.\n if (meta.checksumSha256 !== undefined) {\n const actual = createHash(\"sha256\").update(stored).digest(\"base64\");\n if (actual !== meta.checksumSha256) {\n return yield* Effect.fail(\n new UserError({ message: `checksum mismatch: stored blob hashes to ${actual}, server declared ${meta.checksumSha256}` }),\n );\n }\n }\n\n // Decrypt when the marker names a key; plaintext otherwise.\n let bytes: Uint8Array = stored;\n if (meta.encryption !== undefined) {\n const passwords = [...args.password] as PasswordFlag[];\n const barePasswords = passwords.filter((p): p is string => typeof p === \"string\");\n const passwordSalt =\n barePasswords.length > 0 && cred.kind === \"personal\"\n ? yield* Effect.tryPromise({\n try: async () => (await fetchUserInfo({ baseUrl: new URL(baseUrl), apiToken: cred.apiToken, fetch })).passwordSalt,\n catch: (e) => new UserError({ message: `fetching the password salt failed: ${e instanceof Error ? e.message : String(e)}` }),\n })\n : undefined;\n const key = yield* Effect.tryPromise({\n try: async () => {\n const ring = await Keyring.build({ passwords: [], topics: [], ...(orgKeys !== undefined ? { orgMasterKeys: orgKeys } : {}) });\n for (const p of passwords) {\n if (typeof p !== \"string\") ring.add(await deriveKey(p[0], p[1]));\n else if (passwordSalt !== undefined) ring.add(await deriveKey(p, passwordSalt));\n }\n return ring.keyForMarker(meta.encryption as never);\n },\n catch: (e) => new UserError({ message: `building the keyring failed: ${e instanceof Error ? e.message : String(e)}` }),\n });\n if (key === undefined) {\n return yield* Effect.fail(\n new UserError({\n message:\n meta.encryption.type === \"org\"\n ? `the file is sealed under org master_key v${meta.encryption.v} — unlock the vault (org session) to decrypt`\n : \"the file is encrypted — pass the matching -p/--password (`pw@topic`, or a bare account password)\",\n }),\n );\n }\n bytes = yield* Effect.tryPromise({\n try: () => decryptBytes(key, stored),\n catch: (e) => new UserError({ message: `decryption failed: ${e instanceof Error ? e.message : String(e)}` }),\n });\n }\n\n const filename = meta.filename ?? args.fileId;\n const target = yield* Effect.tryPromise({\n try: async () => {\n const t = await resolveTarget(Option.getOrUndefined(args.out), filename);\n await writeFile(t, bytes);\n return t;\n },\n catch: (e) => new UserError({ message: `saving failed: ${e instanceof Error ? e.message : String(e)}` }),\n });\n\n if (args.format === \"json\") {\n yield* out.print(\n JSON.stringify({\n type: \"downloaded\",\n id: args.fileId,\n path: target,\n filename: meta.filename ?? null,\n contentType: meta.contentType ?? null,\n size: meta.size ?? null,\n }),\n );\n } else {\n yield* out.print(target);\n }\n }),\n);\n","// Format events according to the user-selected `--format`. Pure: returns the\n// stdout line (sans trailing newline); printing is CliOutput's job.\n\nimport type { Event } from \"@simplepush/sdk\";\n\nexport type Format = \"json\" | \"pretty\" | \"raw\";\n\nexport function formatEvent(event: Event, format: Format, decrypted: unknown | undefined): string {\n switch (format) {\n case \"json\": {\n const value: Record<string, unknown> = { ...event };\n if (decrypted !== undefined) value.decrypted = decrypted;\n return JSON.stringify(value);\n }\n case \"pretty\": {\n const lines: string[] = [`=== ${event.eventType} ===`];\n if (event.createdAt) lines.push(` at: ${event.createdAt}`);\n if (event.streamId) lines.push(` stream: ${event.streamId}`);\n if (event.actor) {\n const a = event.actor;\n lines.push(` actor: ${a.name ? `${a.name} (${a.publicId})` : a.publicId}`);\n if (a.devicePublicId || a.deviceName) {\n lines.push(` device: ${a.deviceName ? `${a.deviceName} (${a.devicePublicId ?? \"?\"})` : a.devicePublicId}`);\n }\n }\n if (event.encryption) {\n const enc = event.encryption.type === \"personal\"\n ? `personal (${event.encryption.keyFingerprint})`\n : `org (v${event.encryption.v})`;\n lines.push(` encryption: ${enc}`);\n }\n if (decrypted !== undefined) lines.push(` decrypted: ${prettyValue(decrypted)}`);\n else lines.push(` data: ${prettyValue(event.data)}`);\n return lines.join(\"\\n\") + \"\\n\";\n }\n case \"raw\": {\n const payload = decrypted ?? event.data;\n return extractRaw(payload) ?? JSON.stringify(payload);\n }\n }\n}\n\nfunction prettyValue(v: unknown): string {\n try { return JSON.stringify(v, null, 2); }\n catch { return String(v); }\n}\n\nfunction extractRaw(v: unknown): string | undefined {\n if (!v || typeof v !== \"object\") return undefined;\n const obj = v as Record<string, unknown>;\n for (const k of [\"text\", \"value\", \"selectedValue\", \"url\", \"presignedGetUrl\", \"objectKey\"]) {\n const x = obj[k];\n if (typeof x === \"string\") return x;\n }\n return undefined;\n}\n","// `sp events` — stream the raw event feed as a Stream pipeline:\n//\n// source ─ halt (interruptWhen) ─ --until (takeWhile) ─ --type (filter)\n// ─ decrypt+print (mapEffect) ─ --limit (take) ─ runDrain\n//\n// Terminations that originate on OUR side (quiet-exit after a replay drains,\n// --limit reached) complete the `halt` deferred, which interrupts the whole\n// pipeline — including a socket read already in flight. (The previous\n// timeoutTo/take-only shape hung once events had actually flowed: the pending\n// WebSocket pull was never interrupted.) Interruption unwinds the scope, which\n// aborts the SDK stream via its AbortSignal (see services/sdk.ts).\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Deferred, Effect, Option, Ref, Stream } from \"effect\";\nimport { TypeFilter, tryDecryptEventData, type Event } from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n mappedText,\n passwordOption,\n quietOption,\n topicOption,\n} from \"../global-options.js\";\nimport { loadOrgMasterKeys, resolveCredential } from \"./collect.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { acquireClient, acquireOrgClient, sdkCall, sdkStream } from \"../services/sdk.js\";\nimport { formatEvent, type Format } from \"../output.js\";\nimport { sharedWebSocketFactory } from \"../daemon/transport.js\";\nimport { resolveSince, resolveUntil } from \"../since.js\";\n\nconst eventTypeOption = Options.text(\"type\").pipe(\n Options.withDescription(\"Filter by event type. Repeatable.\"),\n Options.repeated,\n);\n\nconst sinceOption = mappedText(\"since\", resolveSince).pipe(\n Options.withDescription(\"Replay from this point. Accepts `24h`, `7d`, or an ISO 8601 timestamp.\"),\n Options.optional,\n);\n\nconst untilOption = mappedText(\"until\", resolveUntil).pipe(\n Options.withDescription(\"Stop at this timestamp. Forces a finite range, so `--follow` is ignored.\"),\n Options.optional,\n);\n\nconst limitOption = Options.integer(\"limit\").pipe(\n Options.withDescription(\"Maximum number of events to print, then exit.\"),\n Options.optional,\n);\n\nconst followOption = Options.boolean(\"follow\").pipe(\n Options.withAlias(\"f\"),\n Options.withDescription(\"After history is drained, keep streaming live instead of exiting. Only meaningful with `--since`.\"),\n);\n\nconst formatOption = Options.choice(\"format\", [\"json\", \"pretty\", \"raw\"] as const).pipe(\n Options.withDescription(\"Output format.\"),\n Options.withDefault(\"json\" as Format),\n);\n\nconst directOption = Options.boolean(\"direct\").pipe(\n Options.withDescription(\"Open an independent WS connection. By default a LIVE stream (no --since) shares ONE broker connection across all sp processes; --direct bypasses it. A --since history replay always goes direct.\"),\n);\n\nconst QUIET_EXIT_MS = 2_000;\n\nexport const eventsCommand = Command.make(\n \"events\",\n {\n type: eventTypeOption,\n since: sinceOption,\n until: untilOption,\n limit: limitOption,\n follow: followOption,\n format: formatOption,\n direct: directOption,\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n // Personal stream with an API token (flag or $SP_API_TOKEN); otherwise\n // the saved CLI session streams the ORG-wide feed at\n // /ws/v1/events/organization — same credential precedence as `collect`.\n const cred = yield* resolveCredential(args[\"api-token\"], args[\"base-url\"]);\n const baseUrl = cred.baseUrl;\n\n const sinceIso = Option.getOrUndefined(args.since);\n const untilDate = Option.getOrUndefined(args.until);\n const limit = Option.getOrUndefined(args.limit);\n\n const filter = new TypeFilter(args.type);\n yield* Effect.forEach(filter.unknown, (u) => out.warn(`ignoring unknown --type \\`${u}\\``));\n\n // A live stream (no --since) shares the broker by default; a historical\n // replay (--since) goes direct — the broker only holds a recent window,\n // so it can't serve a deep backlog. `--direct` forces direct either way.\n const webSocketFactory =\n args.direct || sinceIso !== undefined\n ? Option.none()\n : yield* sharedWebSocketFactory({\n credential:\n cred.kind === \"personal\"\n ? { kind: \"personal\", apiToken: cred.apiToken }\n : { kind: \"org\", bearer: cred.bearer },\n baseUrl,\n });\n const factoryConfig = Option.match(webSocketFactory, { onNone: () => ({}), onSome: (f) => ({ webSocketFactory: f }) });\n\n const orgKeys = cred.kind === \"org\" ? yield* loadOrgMasterKeys : undefined;\n if (cred.kind === \"org\") yield* out.info(`streaming via org session (${baseUrl})`);\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client =\n cred.kind === \"personal\"\n ? yield* acquireClient({\n baseUrl,\n apiToken: cred.apiToken,\n passwords: args.password,\n ...factoryConfig,\n })\n : yield* acquireOrgClient({\n baseUrl,\n bearerToken: cred.bearer,\n ...(orgKeys !== undefined ? { orgMasterKeys: orgKeys } : {}),\n ...factoryConfig,\n });\n\n // Personal: includePasswordSalt also derives the account default key\n // from a bare `--password`, so submission bodies decrypt too. Org:\n // the keyring is the cached vault's master keys.\n const keyring =\n cred.kind === \"org\"\n ? orgKeys !== undefined\n ? yield* sdkCall(\"keyring\", () => client.keyring())\n : undefined\n : args.password.length > 0\n ? yield* sdkCall(\"keyring\", () => client.keyring({ includePasswordSalt: true }))\n : undefined;\n // `keyring.size` counts fingerprint-matched personal/topic keys; org\n // master keys are version-matched and invisible to it.\n if (keyring && cred.kind === \"org\") {\n yield* out.info(`keyring built with ${orgKeys!.length} org master key(s)`);\n } else if (keyring) {\n if (keyring.size > 0) yield* out.info(`keyring built with ${keyring.size} fingerprint(s)`);\n else yield* out.warn(`no symmetric keys derived from --password (count=${args.password.length})`);\n }\n\n const wsPath = cred.kind === \"org\" ? \"/ws/v1/events/organization\" : \"/ws/v1/events\";\n yield* out.info(\n `connecting to ${baseUrl.replace(/\\/+$/, \"\")}${wsPath}${sinceIso ? `?since=${sinceIso}` : \"\"}`,\n );\n\n // A pure history replay exits shortly after the backlog drains.\n const exitWhenQuiet = sinceIso !== undefined && !args.follow && untilDate === undefined;\n const printed = yield* Ref.make(0);\n const endNote = yield* Ref.make(Option.none<string>());\n const note = (msg: string) => Ref.set(endNote, Option.some(msg));\n const halt = yield* Deferred.make<void>();\n const lastActivity = yield* Ref.make(Date.now());\n\n // Inactivity watchdog for the quiet-exit: any source element (even a\n // type-filtered one) resets the clock; QUIET_EXIT_MS of silence\n // completes `halt`, the one teardown signal for the whole pipeline.\n if (exitWhenQuiet) {\n yield* Effect.forkScoped(\n Effect.gen(function* () {\n while (true) {\n yield* Effect.sleep(\"250 millis\");\n const last = yield* Ref.get(lastActivity);\n if (Date.now() - last >= QUIET_EXIT_MS) {\n yield* note(\"history drained, exiting (use --follow to keep streaming)\");\n yield* Deferred.succeed(halt, void 0);\n return;\n }\n }\n }),\n );\n }\n\n const untilReached = (ev: Event): boolean => {\n if (untilDate === undefined || !ev.createdAt) return false;\n const t = new Date(ev.createdAt);\n return Number.isFinite(t.getTime()) && t >= untilDate;\n };\n\n // `halt` must end a socket read already in flight — stream-level\n // interruption alone waits for the pending pull, which never\n // resolves on a quiet live socket. Aborting the WS is the lever the\n // SDK honors mid-read, so halt trips this controller directly.\n const endController = new AbortController();\n yield* Effect.forkScoped(\n Deferred.await(halt).pipe(Effect.andThen(Effect.sync(() => endController.abort()))),\n );\n\n const source = sdkStream(\"events stream\", (signal) =>\n client.events({\n ...(sinceIso !== undefined ? { since: sinceIso } : {}),\n signal: AbortSignal.any([signal, endController.signal]),\n }),\n );\n\n yield* source.pipe(\n Stream.tap(() => Ref.set(lastActivity, Date.now())),\n // Our own end conditions (quiet-exit, --limit, --until) complete\n // `halt`; interruptWhen tears down the pipeline even when a socket\n // read is pending.\n Stream.interruptWhen(Deferred.await(halt)),\n untilDate !== undefined\n ? Stream.takeUntilEffect((ev: Event) =>\n untilReached(ev)\n ? note(\"--until reached, exiting\").pipe(\n Effect.zipRight(Deferred.succeed(halt, void 0)),\n Effect.as(true),\n )\n : Effect.succeed(false),\n )\n : (s) => s,\n // takeUntil emits the boundary element too; --until is exclusive.\n Stream.filter((ev) => !untilReached(ev) && filter.matches(ev)),\n Stream.mapEffect((ev) =>\n Effect.gen(function* () {\n const decrypted = keyring\n ? yield* sdkCall(\"decrypt event\", () => tryDecryptEventData(ev, keyring))\n : undefined;\n yield* out.print(formatEvent(ev, args.format, decrypted));\n const n = yield* Ref.updateAndGet(printed, (x) => x + 1);\n if (limit !== undefined && n >= limit) {\n yield* note(`--limit ${limit} reached, exiting`);\n yield* Deferred.succeed(halt, void 0);\n }\n }),\n ),\n limit !== undefined ? Stream.take(limit) : (s) => s,\n Stream.runDrain,\n // The abort surfaces as a stream failure; when WE initiated it\n // (halt completed), that failure is a clean exit, not an error.\n Effect.catchAll((e) =>\n Effect.flatMap(Deferred.isDone(halt), (done) => (done ? Effect.void : Effect.fail(e))),\n ),\n );\n\n yield* Ref.get(endNote).pipe(\n Effect.flatMap(Option.match({ onNone: () => Effect.void, onSome: (msg) => out.info(msg) })),\n );\n const total = yield* Ref.get(printed);\n if (args.format === \"raw\" && total === 0) yield* out.warn(\"no events matched\");\n }),\n );\n }),\n);\n","// Parse `--text-input \"desc;required=true\"` style values into wire `Input`s.\n\nimport type { Action, Input } from \"@simplepush/sdk\";\n\nexport type InputSpec = {\n description?: string;\n required: boolean;\n defaultValue?: string;\n // Choice-only multi-select settings. `multi` defaults to false (single\n // choice); `minSelections`/`maxSelections` are only meaningful when multi.\n multi?: boolean;\n minSelections?: number;\n maxSelections?: number;\n};\n\nexport type InputKind = \"text\" | \"choice\" | \"actions\" | \"photo\" | \"voiceRecording\" | \"file\" | \"location\";\n\nconst SETTING_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;\n\nfunction looksLikeSetting(seg: string): boolean {\n return SETTING_RE.test(seg);\n}\n\nfunction supportedKeys(kind: InputKind): string[] {\n if (kind === \"text\") return [\"required\", \"defaultValue\"];\n if (kind === \"choice\") return [\"required\", \"multi\", \"minSelections\", \"maxSelections\"];\n return [\"required\"];\n}\n\nfunction parseBool(s: string): boolean {\n if (s === \"true\" || s === \"yes\" || s === \"1\") return true;\n if (s === \"false\" || s === \"no\" || s === \"0\") return false;\n throw new Error(`expected boolean (true/false), got \\`${s}\\``);\n}\n\nfunction parseCount(s: string, floor: number): number {\n const n = Number(s);\n if (!Number.isInteger(n) || n < floor) throw new Error(`expected an integer >= ${floor}, got \\`${s}\\``);\n return n;\n}\n\nfunction applySetting(spec: InputSpec, seg: string, kind: InputKind): void {\n const eq = seg.indexOf(\"=\");\n if (eq < 0) throw new Error(`expected \\`key=value\\` segment, got \\`${seg}\\``);\n const key = seg.slice(0, eq).trim();\n const value = seg.slice(eq + 1).trim();\n if (key === \"required\") {\n spec.required = parseBool(value);\n return;\n }\n if (key === \"defaultValue\") {\n if (kind !== \"text\") throw new Error(\"`defaultValue=` is not supported on this input type (only --text-input)\");\n spec.defaultValue = value;\n return;\n }\n if (key === \"multi\") {\n if (kind !== \"choice\") throw new Error(\"`multi=` is not supported on this input type (only --choice-input)\");\n spec.multi = parseBool(value);\n return;\n }\n if (key === \"minSelections\") {\n if (kind !== \"choice\") throw new Error(\"`minSelections=` is not supported on this input type (only --choice-input)\");\n // 0 is allowed: it permits a deliberate empty answer (overrides the\n // required-implied floor of 1).\n spec.minSelections = parseCount(value, 0);\n return;\n }\n if (key === \"maxSelections\") {\n if (kind !== \"choice\") throw new Error(\"`maxSelections=` is not supported on this input type (only --choice-input)\");\n spec.maxSelections = parseCount(value, 1);\n return;\n }\n throw new Error(`unknown input setting \\`${key}=\\`; supported: ${supportedKeys(kind).join(\", \")}`);\n}\n\n// Splits `s` on unescaped `sep`. `\\<sep>` and `\\\\` unescape. Other backslashes pass through.\nfunction splitUnescaped(s: string, sep: string): string[] {\n const out: string[] = [];\n let cur = \"\";\n for (let i = 0; i < s.length; i++) {\n const c = s[i]!;\n if (c === \"\\\\\") {\n const next = s[i + 1];\n if (next === sep || next === \"\\\\\") { cur += next; i += 1; continue; }\n cur += c;\n continue;\n }\n if (c === sep) { out.push(cur); cur = \"\"; continue; }\n cur += c;\n }\n out.push(cur);\n return out;\n}\n\nexport function parseInputSpec(raw: string, kind: InputKind): InputSpec {\n const segments = splitUnescaped(raw, \";\");\n const first = segments.shift() ?? \"\";\n const spec: InputSpec = { required: true };\n if (first !== \"\") spec.description = first;\n for (const seg of segments) applySetting(spec, seg, kind);\n return spec;\n}\n\nexport function parseChoiceSpec(raw: string): { spec: InputSpec; options: string[] } {\n const segments = splitUnescaped(raw, \";\");\n const nonSettings: string[] = [];\n const settings: string[] = [];\n for (const seg of segments) (looksLikeSetting(seg) ? settings : nonSettings).push(seg);\n\n let description: string | undefined;\n let optionsRaw: string;\n if (nonSettings.length === 0) {\n throw new Error(\"--choice-input requires a comma-separated options list\");\n } else if (nonSettings.length === 1) {\n optionsRaw = nonSettings[0]!;\n } else if (nonSettings.length === 2) {\n description = nonSettings[0] !== \"\" ? nonSettings[0] : undefined;\n optionsRaw = nonSettings[1]!;\n } else {\n throw new Error(\"--choice-input has too many `;`-separated non-setting segments (expected `[description;]options[;key=value...]`)\");\n }\n\n const options = optionsRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (options.length === 0) throw new Error(\"--choice-input options list is empty\");\n\n const spec: InputSpec = { required: true };\n if (description !== undefined) spec.description = description;\n for (const seg of settings) applySetting(spec, seg, \"choice\");\n return { spec, options };\n}\n\nconst VALID_ACTION_STYLES = new Set([\"default\", \"primary\", \"destructive\"]);\n\n// `key=Label[:style]`. Split on the FIRST `=` (key vs the rest) and the LAST `:`\n// (label vs style) — but only peel off `:style` when the suffix is an actual\n// style, so a label legitimately containing a colon (\"Open: settings\") survives.\nfunction parseActionToken(token: string): Action {\n const eq = token.indexOf(\"=\");\n if (eq < 0) throw new Error(`each action must be \\`key=Label[:style]\\`, got \\`${token}\\``);\n const key = token.slice(0, eq).trim();\n let label = token.slice(eq + 1).trim();\n if (key.length === 0) throw new Error(`action key must not be empty in \\`${token}\\``);\n if (label.length === 0) throw new Error(`action label must not be empty in \\`${token}\\``);\n let style: Action[\"style\"] | undefined;\n const lastColon = label.lastIndexOf(\":\");\n if (lastColon >= 0) {\n const maybe = label.slice(lastColon + 1).trim();\n if (VALID_ACTION_STYLES.has(maybe)) {\n style = maybe as Action[\"style\"];\n label = label.slice(0, lastColon).trim();\n if (label.length === 0) throw new Error(`action label must not be empty in \\`${token}\\``);\n }\n }\n return style ? { key, label, style } : { key, label };\n}\n\n// `[description;]key=Label[:style],...[;required=...]`. Each `key=Label` action\n// token matches the generic SETTING_RE, so actions can't be detected that way —\n// only `required=` is treated as a setting; everything else is positional\n// (description / action-list) exactly like a choice spec.\nexport function parseActionsSpec(raw: string): { spec: InputSpec; actions: Action[] } {\n const segments = splitUnescaped(raw, \";\");\n const settings: string[] = [];\n const nonSettings: string[] = [];\n for (const seg of segments) (/^\\s*required\\s*=/.test(seg) ? settings : nonSettings).push(seg);\n\n let description: string | undefined;\n let actionsRaw: string;\n if (nonSettings.length === 0) {\n throw new Error(\"--action-input requires a comma-separated `key=Label[:style]` list\");\n } else if (nonSettings.length === 1) {\n actionsRaw = nonSettings[0]!;\n } else if (nonSettings.length === 2) {\n description = nonSettings[0] !== \"\" ? nonSettings[0] : undefined;\n actionsRaw = nonSettings[1]!;\n } else {\n throw new Error(\"--action-input has too many `;`-separated non-setting segments (expected `[description;]key=Label[:style],...[;required=...]`)\");\n }\n\n const actions = splitUnescaped(actionsRaw, \",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n .map(parseActionToken);\n if (actions.length === 0) throw new Error(\"--action-input list is empty\");\n // Keys are E2E-encrypted before send, so the backend can't enforce uniqueness;\n // catch it here for a clear error (parseActionToken already rejects empties).\n const seenKeys = new Set<string>();\n for (const a of actions) {\n if (seenKeys.has(a.key)) throw new Error(`duplicate action key: \\`${a.key}\\``);\n seenKeys.add(a.key);\n }\n\n const spec: InputSpec = { required: true };\n if (description !== undefined) spec.description = description;\n for (const seg of settings) applySetting(spec, seg, \"actions\");\n return { spec, actions };\n}\n\nexport type SliderConfig = { min: number; max: number; step?: number; unit?: string; defaultValue?: number };\n\nfunction parseNum(s: string, name: string): number {\n const n = Number(s);\n if (!Number.isFinite(n)) throw new Error(`slider \\`${name}\\` must be a number, got \\`${s}\\``);\n return n;\n}\n\n// `[description;]min=..;max=..[;step=..][;unit=..][;default=..][;required=..]`.\n// The description is the first segment only when it isn't a `key=value` setting;\n// `min`/`max` are required.\nexport function parseSliderSpec(raw: string): { spec: InputSpec; slider: SliderConfig } {\n const segments = splitUnescaped(raw, \";\");\n let description: string | undefined;\n const settings: string[] = [];\n segments.forEach((seg, i) => {\n if (i === 0 && !looksLikeSetting(seg)) {\n if (seg !== \"\") description = seg;\n } else {\n settings.push(seg);\n }\n });\n\n let min: number | undefined, max: number | undefined, step: number | undefined, defaultValue: number | undefined;\n let unit: string | undefined;\n let required = true;\n for (const seg of settings) {\n const eq = seg.indexOf(\"=\");\n if (eq < 0) throw new Error(`expected \\`key=value\\` segment, got \\`${seg}\\``);\n const key = seg.slice(0, eq).trim();\n const value = seg.slice(eq + 1).trim();\n switch (key) {\n case \"min\": min = parseNum(value, \"min\"); break;\n case \"max\": max = parseNum(value, \"max\"); break;\n case \"step\": step = parseNum(value, \"step\"); break;\n case \"unit\": unit = value; break;\n case \"default\": defaultValue = parseNum(value, \"default\"); break;\n case \"required\": required = parseBool(value); break;\n default: throw new Error(`unknown slider setting \\`${key}=\\`; supported: min, max, step, unit, default, required`);\n }\n }\n if (min === undefined || max === undefined) throw new Error(\"--slider-input requires `min=` and `max=`\");\n if (min >= max) throw new Error(\"--slider-input `min` must be less than `max`\");\n if (step !== undefined && step <= 0) throw new Error(\"--slider-input `step` must be positive\");\n if (defaultValue !== undefined && (defaultValue < min || defaultValue > max)) {\n throw new Error(\"--slider-input `default` must be within [min, max]\");\n }\n\n const spec: InputSpec = { required };\n if (description !== undefined) spec.description = description;\n const slider: SliderConfig = {\n min,\n max,\n ...(step !== undefined ? { step } : {}),\n ...(unit !== undefined && unit !== \"\" ? { unit } : {}),\n ...(defaultValue !== undefined ? { defaultValue } : {}),\n };\n return { spec, slider };\n}\n\n// --- spec -> wire `Input` builders ----------------------------------------\n// Shared by both `sp task` and `sp subtask` so the two commands send identical\n// input JSON and can't drift apart.\n// These build the PLAINTEXT input objects; the SDK encrypts each field\n// (description / options / labels / slider config) on send, identically for\n// tasks and subtasks.\n\nfunction textInputJson(spec: InputSpec): Input {\n const out: Input = { type: \"text\", required: spec.required };\n if (spec.description !== undefined) out.description = spec.description;\n if (spec.defaultValue !== undefined) out.defaultValue = spec.defaultValue;\n return out;\n}\n\nfunction simpleInputJson(\n type: \"photo\" | \"voiceRecording\" | \"file\" | \"location\",\n spec: InputSpec,\n): Input {\n const out: Input = { type, required: spec.required };\n if (spec.description !== undefined) out.description = spec.description;\n return out;\n}\n\nfunction choiceInputJson(spec: InputSpec, options: string[]): Input {\n const out: Input = { type: \"choice\", required: spec.required, options };\n if (spec.description !== undefined) out.description = spec.description;\n // Multi-select (omit `multi` when false, like the wire contract); the\n // min/max caps are only meaningful when multi.\n if (spec.multi) out.multi = true;\n if (spec.minSelections !== undefined) out.minSelections = spec.minSelections;\n if (spec.maxSelections !== undefined) out.maxSelections = spec.maxSelections;\n return out;\n}\n\nfunction sliderInputJson(spec: InputSpec, slider: SliderConfig): Input {\n const out: Input = {\n type: \"slider\",\n required: spec.required,\n min: slider.min,\n max: slider.max,\n ...(slider.step !== undefined ? { step: slider.step } : {}),\n ...(slider.unit !== undefined ? { unit: slider.unit } : {}),\n ...(slider.defaultValue !== undefined ? { defaultValue: slider.defaultValue } : {}),\n };\n if (spec.description !== undefined) out.description = spec.description;\n return out;\n}\n\nfunction actionsInputJson(spec: InputSpec, actions: Action[]): Input {\n const out: Input = { type: \"actions\", required: spec.required, actions };\n if (spec.description !== undefined) out.description = spec.description;\n return out;\n}\n\n/** Raw values of the repeatable `--*-input` flags. Each field is optional so a\n * caller that doesn't expose a given flag (or just got none) can omit it. */\nexport type InputArgs = {\n \"text-input\"?: ReadonlyArray<string>;\n \"choice-input\"?: ReadonlyArray<string>;\n \"action-input\"?: ReadonlyArray<string>;\n \"slider-input\"?: ReadonlyArray<string>;\n \"photo-input\"?: ReadonlyArray<string>;\n \"voice-recording-input\"?: ReadonlyArray<string>;\n \"file-input\"?: ReadonlyArray<string>;\n \"location-input\"?: ReadonlyArray<string>;\n};\n\n/** Parse every `--*-input` flag into the ordered `Input[]` sent on the wire.\n * Shared between `sp task` and `sp subtask`. Inputs are emitted grouped by kind\n * (text, choice, actions, slider, photo, voice, file, location). */\nexport function buildInputs(args: InputArgs): Input[] {\n const out: Input[] = [];\n for (const raw of args[\"text-input\"] ?? []) out.push(textInputJson(parseInputSpec(raw, \"text\")));\n for (const raw of args[\"choice-input\"] ?? []) {\n const { spec, options } = parseChoiceSpec(raw);\n out.push(choiceInputJson(spec, options));\n }\n for (const raw of args[\"action-input\"] ?? []) {\n const { spec, actions } = parseActionsSpec(raw);\n out.push(actionsInputJson(spec, actions));\n }\n for (const raw of args[\"slider-input\"] ?? []) {\n const { spec, slider } = parseSliderSpec(raw);\n out.push(sliderInputJson(spec, slider));\n }\n for (const raw of args[\"photo-input\"] ?? []) out.push(simpleInputJson(\"photo\", parseInputSpec(raw, \"photo\")));\n for (const raw of args[\"voice-recording-input\"] ?? []) out.push(simpleInputJson(\"voiceRecording\", parseInputSpec(raw, \"voiceRecording\")));\n for (const raw of args[\"file-input\"] ?? []) out.push(simpleInputJson(\"file\", parseInputSpec(raw, \"file\")));\n for (const raw of args[\"location-input\"] ?? []) out.push(simpleInputJson(\"location\", parseInputSpec(raw, \"location\")));\n return out;\n}\n","// `sp notify` — admin-side notification send for org-bound CLIs. Targets\n// either a named member, a broadcast, or an org topic. Auto-encrypts under\n// the org master_key when the local vault is unlocked, falling back to\n// plaintext otherwise (or when --no-encrypt is passed).\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Effect, Option } from \"effect\";\nimport {\n type OrgSendTarget,\n type NotificationInput,\n} from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n requireApiToken,\n topicOption,\n willEncrypt,\n} from \"../global-options.js\";\nimport { UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { formatSent, type Member } from \"../collect-output.js\";\nimport { Api } from \"../services/api.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { acquireClient, acquireOrgClient, sdkCall } from \"../services/sdk.js\";\nimport { bearerToken } from \"../services/stores.js\";\nimport { parseActionsSpec } from \"../input-spec.js\";\n\n// Supported notification media content types (mirror the backend allow-list /\n// iOS UTIs) + extension → MIME. image/* renders on iOS + Android; audio/* is iOS-only.\nconst NOTIFY_MEDIA_TYPES: Record<\"image\" | \"audio\", Set<string>> = {\n image: new Set([\"image/jpeg\", \"image/png\", \"image/gif\"]),\n audio: new Set([\"audio/aiff\", \"audio/x-aiff\", \"audio/wav\", \"audio/x-wav\", \"audio/vnd.wave\", \"audio/mpeg\", \"audio/mp3\", \"audio/mp4\", \"audio/aac\", \"audio/x-m4a\"]),\n};\nconst NOTIFY_MEDIA_EXT: Record<string, string> = {\n png: \"image/png\", jpg: \"image/jpeg\", jpeg: \"image/jpeg\", gif: \"image/gif\",\n aiff: \"audio/aiff\", aif: \"audio/aiff\", wav: \"audio/wav\", mp3: \"audio/mpeg\", m4a: \"audio/mp4\", aac: \"audio/aac\",\n};\n\n/** Derive + validate the media content type from a URL's extension, or null if\n * unsupported for the kind. */\nfunction notifyMediaContentType(url: string, kind: \"image\" | \"audio\"): string | null {\n const clean = url.split(\"?\")[0] ?? url;\n const ext = clean.slice(clean.lastIndexOf(\".\") + 1).toLowerCase();\n const ct = NOTIFY_MEDIA_EXT[ext];\n return ct && NOTIFY_MEDIA_TYPES[kind].has(ct) ? ct : null;\n}\n\nconst contentOption = Options.text(\"content\").pipe(\n Options.withDescription(\"Notification body. Encrypted under the org master_key when the vault is unlocked, plaintext otherwise.\"),\n);\n\n// No `-t` short alias: `-t` is the shared `--api-token` flag (see global-options),\n// used by the personal send path. Use `--title` for the notification title.\nconst titleOption = Options.text(\"title\").pipe(\n Options.withDescription(\"Optional notification title shown above the body on the recipient's lock screen.\"),\n Options.optional,\n);\n\nconst memberOption = Options.text(\"member\").pipe(\n Options.withAlias(\"m\"),\n Options.withDescription(\"Send to a single org member by display name (case-insensitive). Org send; mutually exclusive with --broadcast, --org-topic, and -k/--topic.\"),\n Options.optional,\n);\n\nconst broadcastOption = Options.boolean(\"broadcast\").pipe(\n Options.withAlias(\"b\"),\n Options.withDescription(\"Send to every member of the org. Org send; mutually exclusive with --member, --org-topic, and -k/--topic.\"),\n);\n\nconst orgTopicOption = Options.text(\"org-topic\").pipe(\n Options.withAlias(\"o\"),\n Options.withDescription(\"Send to an org topic by value (from `sp org topics list`). Org send; mutually exclusive with --member, --broadcast, and -k/--topic (the personal topic).\"),\n Options.optional,\n);\n\nconst tagOption = Options.text(\"tag\").pipe(\n Options.withDescription(\"Optional notification tag — recipients can use it to coalesce / replace prior notifications with the same tag.\"),\n Options.optional,\n);\n\nconst imageOption = Options.text(\"image\").pipe(\n Options.withDescription(\"Image URL to show in the push (PNG/JPEG/GIF). Renders on iOS + Android. Mutually exclusive with --audio. URLs only — file uploads are SDK-only.\"),\n Options.optional,\n);\n\nconst audioOption = Options.text(\"audio\").pipe(\n Options.withDescription(\"Audio URL to play inline in the push (AIFF/WAV/MP3/M4A; iOS only). Mutually exclusive with --image. URLs only.\"),\n Options.optional,\n);\n\n// Explicit opt-out: even with an unlocked vault, don't encrypt. Useful for\n// sanity-checking the plaintext path, or for org broadcasts that should remain\n// readable by API integrations on the receiving end.\nconst noEncryptOption = Options.boolean(\"no-encrypt\").pipe(\n Options.withDescription(\"Send the body in plaintext even when an unlocked vault is available.\"),\n);\n\n// A notification Action input: tap-buttons (e.g. Accept/Deny) the recipient\n// answers with. A notification carries at most one input, so this is a single\n// option (not repeatable like `sp task`'s --action-input); it reuses the task\n// action-input parsing grammar, minus the `primary` style (notifications only\n// support default|destructive). On an encrypted send the SDK seals each action's\n// `key` AND `label` (like a choice option, and like `sp task`'s actions input);\n// only `style` stays plaintext.\nconst actionInputOption = Options.text(\"action-input\").pipe(\n Options.withAlias(\"a\"),\n Options.withDescription(\"Add an actions input: tap-buttons the recipient answers with (e.g. Accept/Deny). Format: `[description;]key=Label[:style],...`, actions comma-separated; style is default|destructive. Use `\\\\,` for a literal comma in a label. A notification carries at most one input.\"),\n Options.optional,\n);\n\n// A free-text reply input: the recipient types an answer. Boolean flag (no\n// value); a notification carries at most one input, so it's mutually exclusive\n// with --choice-input / --action-input.\nconst textInputOption = Options.boolean(\"text-input\").pipe(\n Options.withDescription(\"Add a free-text reply input the recipient types an answer into. Mutually exclusive with --choice-input / --action-input (a notification carries at most one input).\"),\n);\n\n// A single-choice input: a comma-separated options list the recipient picks ONE\n// of. Notifications support single-select only (no multi, unlike `sp task`).\nconst choiceInputOption = Options.text(\"choice-input\").pipe(\n Options.withAlias(\"c\"),\n Options.withDescription(\"Add a single-choice input: a comma-separated options list (e.g. \\\"Approve,Deny\\\") the recipient picks one of. Notifications are single-select only. Mutually exclusive with --text-input / --action-input.\"),\n Options.optional,\n);\n\n// Recipient-state model. Default (flag absent) = independent: each recipient\n// gets their OWN notification instance, tied together by a `grpntf_` group.\n// `--shared` = the single shared notification the first reply completes\n// for everyone. Mirrors `sp task`'s --shared.\nconst sharedOption = Options.boolean(\"shared\").pipe(\n Options.withDescription(\"Shared mode: ONE notification all recipients see and answer together (the first reply completes it for everyone). Default (without this flag) is independent mode: every recipient gets their own notification instance under a group.\"),\n);\n\n// stdout shape (mirrors `sp task --format`): `text` prints the bare id\n// (default, human/script), `json` prints a machine-readable `sent` line that\n// `sp collect` consumes to know the members + resume point.\nconst formatOption = Options.choice(\"format\", [\"text\", \"json\"] as const).pipe(\n Options.withDescription(\"stdout format for a send: `text` (the bare id, default) or `json` (a `sent` line piped to `sp collect`).\"),\n Options.withDefault(\"text\"),\n);\n\n/** Parse the (at most one) notification input off the three flags. */\nconst parseNotificationInput = (\n textInputOn: boolean,\n choiceRaw: string | undefined,\n actionRaw: string | undefined,\n): Effect.Effect<NotificationInput | undefined, UserError> =>\n Effect.gen(function* () {\n if ([textInputOn, choiceRaw !== undefined, actionRaw !== undefined].filter(Boolean).length > 1) {\n return yield* Effect.fail(\n new UserError({ message: \"a notification carries at most one input: pass only one of --text-input, --choice-input, or --action-input\" }),\n );\n }\n if (textInputOn) return { type: \"text\" } as const;\n if (choiceRaw !== undefined) {\n const options = choiceRaw.split(\",\").map((o) => o.trim()).filter((o) => o.length > 0);\n if (options.length === 0) {\n return yield* Effect.fail(new UserError({ message: \"--choice-input needs at least one comma-separated option\" }));\n }\n return { type: \"choice\", options } as const;\n }\n if (actionRaw !== undefined) {\n const actions = yield* Effect.try({\n try: () => parseActionsSpec(actionRaw).actions,\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n const primary = actions.find((a) => a.style === \"primary\");\n if (primary !== undefined) {\n return yield* Effect.fail(\n new UserError({ message: `notification action styles must be 'default' or 'destructive' (got 'primary' on key \\`${primary.key}\\`) — 'primary' is task-only` }),\n );\n }\n return {\n type: \"actions\",\n actions: actions.map((a) => ({\n key: a.key,\n label: a.label,\n ...(a.style !== undefined ? { style: a.style as \"default\" | \"destructive\" } : {}),\n })),\n } as const;\n }\n return undefined;\n });\n\nexport const notifyCommand = Command.make(\n \"notify\",\n {\n content: contentOption,\n title: titleOption,\n member: memberOption,\n broadcast: broadcastOption,\n \"org-topic\": orgTopicOption,\n tag: tagOption,\n image: imageOption,\n audio: audioOption,\n \"text-input\": textInputOption,\n \"choice-input\": choiceInputOption,\n \"action-input\": actionInputOption,\n shared: sharedOption,\n format: formatOption,\n noEncrypt: noEncryptOption,\n // Personal send: `-k/--topic` sends to a personal topic; omitting every\n // target is a note-to-self. These carry the personal credential + keys.\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const memberName = Option.getOrUndefined(args.member);\n const orgTopicName = Option.getOrUndefined(args[\"org-topic\"]);\n const personalTopic = args.topic[0];\n const isOrgTarget = memberName !== undefined || args.broadcast || orgTopicName !== undefined;\n // At most one of: org member / broadcast / org-topic, or a personal topic.\n // ZERO targets = a personal note-to-self (your own devices).\n const targetCount = [memberName !== undefined, args.broadcast, orgTopicName !== undefined, personalTopic !== undefined].filter(Boolean).length;\n if (targetCount > 1) {\n return yield* Effect.fail(\n new UserError({ message: \"pass at most one target: -m <member> | -b (broadcast) | -o <org-topic> | -t <topic> (omit all for a self-send)\" }),\n );\n }\n\n const titleOpt = Option.getOrUndefined(args.title);\n const tagOpt = Option.getOrUndefined(args.tag);\n const message = args.content;\n\n // Single optional media (image XOR audio) as a link, validated once for\n // both send paths. The org path wraps it in a `NotificationMedia`; the\n // personal path passes the URL straight to the SDK (which re-validates).\n const imageUrl = Option.getOrUndefined(args.image);\n const audioUrl = Option.getOrUndefined(args.audio);\n if (imageUrl !== undefined && audioUrl !== undefined) {\n return yield* Effect.fail(new UserError({ message: \"only one of --image or --audio may be set\" }));\n }\n const mediaUrl = imageUrl ?? audioUrl;\n const mediaKind = imageUrl !== undefined ? \"image\" : \"audio\";\n if (mediaUrl !== undefined) {\n if (!/^https?:\\/\\//.test(mediaUrl)) {\n return yield* Effect.fail(\n new UserError({ message: \"notification media from the CLI must be an http(s) URL; file uploads aren't supported here (use the SDK)\" }),\n );\n }\n if (notifyMediaContentType(mediaUrl, mediaKind) === null) {\n return yield* Effect.fail(\n new UserError({ message: `--${mediaKind} URL must point to a supported ${mediaKind} type (by extension); got \"${mediaUrl}\"` }),\n );\n }\n }\n\n // At most one notification input: text (free-text reply), choice\n // (single-select options), or actions (tap-buttons). The SDK encrypts the\n // choice options / action keys + labels on an encrypted send; a text input\n // has no payload to encrypt.\n const input = yield* parseNotificationInput(\n args[\"text-input\"],\n Option.getOrUndefined(args[\"choice-input\"]),\n Option.getOrUndefined(args[\"action-input\"]),\n );\n\n if (isOrgTarget) {\n // ---- Organization send: CLI bearer session via the SDK's OrgClient ----\n // Field encryption rides in the SDK, under the current org master_key\n // when the vault is unlocked.\n const api = yield* Api;\n const access = yield* VaultAccess;\n\n const vault = yield* access.forSendOrPlaintext(args.noEncrypt);\n const auth = yield* api.session;\n\n const target: OrgSendTarget =\n memberName !== undefined ? { member: memberName }\n : args.broadcast ? { broadcast: true }\n : { topic: orgTopicName! };\n const orgMasterKeys = vault\n ? [vault.masterKeyCurrent, ...vault.masterKeyHistory].map((k) => ({ version: k.version, key: k.key }))\n : undefined;\n const encNote = vault ? ` (encrypted under master_key v${vault.masterKeyCurrent.version})` : \" (plaintext)\";\n const orgOpts = {\n content: message,\n ...(titleOpt !== undefined ? { title: titleOpt } : {}),\n ...(tagOpt !== undefined ? { tag: tagOpt } : {}),\n ...(input !== undefined ? { input } : {}),\n ...(imageUrl !== undefined ? { image: imageUrl } : {}),\n ...(audioUrl !== undefined ? { audio: audioUrl } : {}),\n };\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireOrgClient({\n baseUrl: auth.baseUrl,\n bearerToken: bearerToken(auth),\n ...(orgMasterKeys !== undefined ? { orgMasterKeys } : {}),\n });\n\n if (args.shared) {\n // Single shared notification: one id, all recipients share state.\n const note = yield* sdkCall(\"notify\", () => client.sendNotification({ ...target, ...orgOpts, shared: true }));\n yield* out.info(`Notification sent${encNote}.`);\n yield* out.info(`Id: ${note.notificationId}`);\n yield* out.info(`Created: ${note.createdAt}`);\n if (args.format === \"json\") {\n yield* out.print(formatSent(undefined, note.createdAt, [{ id: note.notificationId, kind: \"notification\", recipient: null }]));\n } else {\n yield* out.print(note.notificationId);\n }\n return;\n }\n\n // Independent mode (default): one instance per targeted member.\n const group = yield* sdkCall(\"notify\", () => client.sendNotification({ ...target, ...orgOpts }));\n const n = group.instances.length;\n yield* out.info(`notification group created: ${group.groupId} (${n} recipient${n === 1 ? \"\" : \"s\"})${encNote}`);\n yield* Effect.forEach(group.instances, (inst) => {\n const who = inst.recipient ? `${inst.recipient.publicId}${inst.recipient.name ? ` (${inst.recipient.name})` : \"\"}` : \"unknown\";\n return out.info(`instance: ${who} -> ${inst.notificationId}`);\n });\n if (n === 0) yield* out.warn(\"the target has no recipients — the group is empty\");\n if (args.format === \"json\") {\n const members: Member[] = group.instances.map((inst) => ({\n id: inst.notificationId,\n kind: \"notification\",\n recipient: inst.recipient ? { publicId: inst.recipient.publicId, name: inst.recipient.name ?? null } : null,\n }));\n yield* out.print(formatSent(group.groupId, group.createdAt, members));\n } else {\n yield* out.print(group.groupId);\n }\n }),\n );\n return;\n }\n\n // ---- Personal send: a personal topic (-k) or a note-to-self (no target) ----\n // Uses the personal SDK Client (API-Token). A topic send encrypts under the\n // matching `password@topic`; a note-to-self encrypts under the account\n // default password (a bare --password), else plaintext.\n const apiToken = yield* requireApiToken(args[\"api-token\"]);\n const encrypting = willEncrypt(args.password, personalTopic);\n if (encrypting) yield* out.info(\"encrypting outgoing notification (Argon2id, this takes a moment)\");\n const encNote = encrypting ? \" (encrypted)\" : \" (plaintext)\";\n\n // Not annotated as SendNotificationOptions on purpose: that would widen\n // `shared` to boolean and pull `{...baseOpts, topic}` into the union\n // overload. Left inferred (no `shared` key) so the topic send resolves to\n // the NotificationGroup overload. Mirrors `sp task`'s baseOpts.\n const baseOpts = {\n content: message,\n ...(titleOpt !== undefined ? { title: titleOpt } : {}),\n ...(tagOpt !== undefined ? { tag: tagOpt } : {}),\n ...(input !== undefined ? { input } : {}),\n ...(imageUrl !== undefined ? { image: imageUrl } : {}),\n ...(audioUrl !== undefined ? { audio: audioUrl } : {}),\n };\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireClient({ baseUrl: args[\"base-url\"], apiToken, passwords: [...args.password] });\n\n // A bare-id (`text`) or `sent`-line (`json`) print for a single note.\n const printSingle = (note: { notificationId: string; createdAt: string }) =>\n args.format === \"json\"\n ? out.print(formatSent(undefined, note.createdAt, [{ id: note.notificationId, kind: \"notification\", recipient: null }]))\n : out.print(note.notificationId);\n\n if (personalTopic === undefined) {\n // Note to self: no topic → your own devices, a single Notification.\n const note = yield* sdkCall(\"notify\", () => client.sendNotification(baseOpts));\n yield* out.info(`self-send notification sent${encNote}.`);\n yield* out.info(`Id: ${note.notificationId}`);\n yield* printSingle(note);\n } else if (args.shared) {\n // Shared mode: one notification all topic subscribers share.\n const note = yield* sdkCall(\"notify\", () => client.sendNotification({ ...baseOpts, topic: personalTopic, shared: true }));\n yield* out.info(`notification sent${encNote}.`);\n yield* out.info(`Id: ${note.notificationId}`);\n yield* printSingle(note);\n } else {\n // Independent (default): one instance per subscriber under a group.\n const group = yield* sdkCall(\"notify\", () => client.sendNotification({ ...baseOpts, topic: personalTopic }));\n const n = group.instances.length;\n yield* out.info(`notification group created: ${group.groupId} (${n} recipient${n === 1 ? \"\" : \"s\"})${encNote}`);\n yield* Effect.forEach(group.instances, (inst) => {\n const who = `${inst.recipient?.publicId ?? \"unknown\"}${inst.recipient?.name ? ` (${inst.recipient.name})` : \"\"}`;\n return out.info(`instance: ${who} -> ${inst.notificationId}`);\n });\n if (n === 0) yield* out.warn(\"the topic has no recipients — the group is empty\");\n if (args.format === \"json\") {\n const members: Member[] = group.instances.map((inst) => ({\n id: inst.notificationId,\n kind: \"notification\",\n recipient: inst.recipient ? { publicId: inst.recipient.publicId, name: inst.recipient.name ?? null } : null,\n }));\n yield* out.print(formatSent(group.groupId, group.createdAt, members));\n } else {\n yield* out.print(group.groupId);\n }\n }\n }),\n );\n }),\n);\n","// `sp integration` — scoped, long-lived, non-interactive org credentials\n// (`spi_...`): what an unattended process (an MCP server, a CI job) presents\n// instead of the org Api-Key, which is unscoped and only revocable by rotating\n// it for everyone.\n//\n// The token this mints is `spi_<credential>.<seed>`:\n// * the credential half authenticates — the backend stores its hash;\n// * the seed half is an X25519 private key generated HERE and never sent\n// anywhere. The backend receives only the derived pubkey plus the org\n// master keys wrapped to it, so it stores blobs it cannot open.\n//\n// Printed exactly once. There is no recovery path by construction: we hold no\n// seed and the backend holds no credential plaintext. `revoke` + re-`create`\n// is the answer to a lost token.\n\nimport { Args, Command, Options } from \"@effect/cli\";\nimport { Effect, Schema } from \"effect\";\n\nimport { quietOption } from \"../global-options.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { VaultStore } from \"../services/stores.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { Sodium } from \"../crypto/sodium.js\";\nimport type { VaultContents } from \"../crypto/index.js\";\n\n// ---------- HTTP shapes (mirror backend/model/v1/OrgIntegrationModels.scala) ----------\n\nconst WrappedKey = Schema.Struct({ version: Schema.Number, blob: Schema.String });\n\nconst CreateOrgIntegrationResponse = Schema.Struct({\n id: Schema.String,\n credential: Schema.String,\n});\n\nexport const OrgIntegrationSummary = Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n scopes: Schema.Array(Schema.String),\n pubkeyB64: Schema.String,\n wrappedKeys: Schema.Array(WrappedKey),\n createdAt: Schema.String,\n // zio-json omits None fields — absent, never null.\n revokedAt: Schema.optional(Schema.String),\n lastUsedAt: Schema.optional(Schema.String),\n});\nexport type OrgIntegrationSummary = typeof OrgIntegrationSummary.Type;\n\nconst ListOrgIntegrationsResponse = Schema.Struct({\n integrations: Schema.Array(OrgIntegrationSummary),\n});\n\n/** Shared with the org-encryption rotation walk. */\nexport const fetchOrgIntegrations = Effect.gen(function* () {\n const api = yield* Api;\n const { integrations } = yield* api.getJson(\n \"fetch integrations\",\n \"/v1/org/integrations\",\n ListOrgIntegrationsResponse,\n );\n return integrations;\n});\n\n/** Shared with the org-encryption rotation walk. */\nexport const putIntegrationWraps = (id: string, wraps: ReadonlyArray<{ version: number; blob: string }>) =>\n Effect.gen(function* () {\n const api = yield* Api;\n yield* api.put(\"integration wrap\", `/v1/org/integrations/${encodeURIComponent(id)}/wraps`, { wraps });\n });\n\n// ---------- create ----------\n\nconst nameOption = Options.text(\"name\").pipe(\n Options.withDescription(\"Human-readable label, shown in `sp integration list` and nowhere else.\"),\n);\n\nconst scopesOption = Options.text(\"scopes\").pipe(\n Options.withDefault(\"send,events:read\"),\n Options.withDescription(\n \"Comma-separated scopes (send, events:read, files:read). org:admin is refused — administration is a human act.\",\n ),\n);\n\nconst createCommand = Command.make(\"create\", { name: nameOption, scopes: scopesOption, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const access = yield* VaultAccess;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n\n const scopes = args.scopes.split(\",\").map((s) => s.trim()).filter(Boolean);\n\n // Encryption on → the mint also wraps every master-key version to the new\n // integration and pins its pubkey in the vault, which means re-encrypting\n // the vault blob — hence the rotation-grade unlock (always re-prompts;\n // the cache deliberately never holds vault_key). Encryption off → the\n // credential is still useful (scoped auth), just with nothing to wrap.\n const cfg = yield* access.fetchConfig;\n const unlocked = cfg.enabled\n ? yield* access.unlockForRotation.pipe(Effect.map((u) => ({ ...u, cfg })))\n : undefined;\n\n const seed = yield* sodium.randomBytes(32);\n const keypair = yield* sodium.seedKeypair(seed);\n\n const wraps: Array<{ version: number; blob: string }> = [];\n if (unlocked) {\n // ALL versions, not just current: recipients answer under the current\n // key, but history lets the integration read replies to older sends —\n // the same reason devices retain old wraps.\n const versions = [...unlocked.vault.masterKeyHistory, unlocked.vault.masterKeyCurrent];\n for (const mk of versions) {\n const blob = yield* sodium.wrapMasterKey(mk.key, unlocked.vault.adminPrivateKey, keypair.publicKey);\n wraps.push({ version: mk.version, blob: sodium.toB64(blob) });\n }\n }\n\n const made = yield* api.postJson(\"create integration\", \"/v1/org/integrations\", CreateOrgIntegrationResponse, {\n name: args.name,\n scopes,\n pubkeyB64: sodium.toB64(keypair.publicKey),\n wrappedKeys: wraps,\n });\n\n if (unlocked) {\n // Pin the pubkey in the vault so rotation can verify it before wrapping\n // a fresh master key — the backend must never be able to substitute a\n // pubkey it controls and receive the next rotation in the clear.\n const nextVault: VaultContents = {\n ...unlocked.vault,\n integrations: [\n ...unlocked.vault.integrations,\n { id: made.id, pubkeyB64: sodium.toB64(keypair.publicKey), name: args.name },\n ],\n };\n const enabledCfg = yield* access.requireEnabled(unlocked.cfg);\n const newBlob = yield* sodium.encryptVault(nextVault, unlocked.vaultKey);\n yield* api\n .put(\"vault update\", \"/v1/org/encryption/vault\", {\n vaultBlobB64: sodium.toB64(newBlob),\n vaultSaltB64: enabledCfg.vaultSaltB64,\n kdfParams: enabledCfg.kdfParams,\n })\n .pipe(\n Effect.tap(() => vaultStore.save(nextVault)),\n Effect.catchAll(() =>\n out.error(\n \"integration created, but pinning its pubkey in the vault failed — \" +\n \"key rotation will SKIP this integration until a `create` or vault write succeeds again.\",\n ),\n ),\n );\n }\n\n const token = `${made.credential}.${sodium.toB64Url(seed)}`;\n yield* out.info(`Integration '${args.name}' created (id ${made.id}, scopes: ${scopes.join(\" \")}).`);\n yield* out.info(\"This token is shown ONCE and cannot be recovered — store it now:\");\n yield* out.print(token);\n if (!cfg.enabled) {\n yield* out.info(\"Org encryption is not enabled; sends made with this token go out plaintext.\");\n }\n yield* out.info(`Revoke with: sp integration revoke ${made.id}`);\n }),\n);\n\n// ---------- list ----------\n\nconst listCommand = Command.make(\"list\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const integrations = yield* fetchOrgIntegrations;\n if (integrations.length === 0) {\n return yield* out.info(\"No integrations. Mint one with `sp integration create --name <name>`.\");\n }\n for (const i of integrations) {\n const state = i.revokedAt ? `revoked ${i.revokedAt}` : \"active\";\n const used = i.lastUsedAt ? `last used ${i.lastUsedAt}` : \"never used\";\n const keys = i.wrappedKeys.length > 0 ? `keys v${i.wrappedKeys.map((w) => w.version).join(\",v\")}` : \"no keys\";\n yield* out.print(`${i.id} ${i.name} [${i.scopes.join(\" \")}] ${state} ${used} ${keys}`);\n }\n }),\n);\n\n// ---------- revoke ----------\n\nconst idArg = Args.text({ name: \"id\" }).pipe(Args.withDescription(\"Integration id, from `sp integration list`.\"));\n\nconst revokeCommand = Command.make(\"revoke\", { id: idArg, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n yield* api.delete(\"revoke integration\", `/v1/org/integrations/${encodeURIComponent(args.id)}`);\n yield* out.info(\n `Integration ${args.id} revoked — its credential stops working immediately. ` +\n \"It keeps any keys it already unwrapped: if it was compromised, also run `sp org encryption key rotate`.\",\n );\n }),\n);\n\nexport const integrationCommand = Command.make(\"integration\").pipe(\n Command.withSubcommands([createCommand, listCommand, revokeCommand]),\n);\n","// `sp org encryption` — end-to-end encryption administration. The crypto\n// root is the org passphrase: it derives `vault_key` (Argon2id) which encrypts\n// the vault blob the server stores. Admins move between machines by\n// re-entering the passphrase; subsequent encryption-using commands on the\n// same machine prompt only if the local plaintext cache is absent. Members\n// never see the passphrase — they only get a per-device wrap of the org\n// master key after an admin runs `sync`.\n\nimport { Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Schema } from \"effect\";\n\nimport { quietOption } from \"../global-options.js\";\nimport { Aborted } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { InviteStore, VaultStore } from \"../services/stores.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { Sodium } from \"../crypto/sodium.js\";\nimport { DEFAULT_KDF_PARAMS, type MasterKey, type VaultContents } from \"../crypto/index.js\";\nimport { fetchOrgIntegrations, putIntegrationWraps } from \"./integration.js\";\n\n// ---------- HTTP shapes (mirror backend/model/v1/EncryptionModels.scala) ----------\n\n// Wire shape for per-device wrapped keys. Mirrors `domain.WrappedKey` on the\n// backend (version + base64 blob), used both in GET /org/encryption/devices\n// payloads and in PUT .../wraps bodies.\nconst WrappedKey = Schema.Struct({ version: Schema.Number, blob: Schema.String });\ntype WrappedKey = typeof WrappedKey.Type;\n\nconst OrgEncryptionDeviceSummary = Schema.Struct({\n deviceId: Schema.String,\n devicePubkeyB64: Schema.String,\n inviteHmacB64: Schema.String,\n wrappedKeys: Schema.Array(WrappedKey),\n});\ntype OrgEncryptionDeviceSummary = typeof OrgEncryptionDeviceSummary.Type;\n\nconst ListOrgEncryptionDevicesResponse = Schema.Struct({\n devices: Schema.Array(OrgEncryptionDeviceSummary),\n});\n\nconst fetchEncryptionDevices = Effect.gen(function* () {\n const api = yield* Api;\n const { devices } = yield* api.getJson(\n \"fetch encryption devices\",\n \"/v1/org/encryption/devices\",\n ListOrgEncryptionDevicesResponse,\n );\n return devices;\n});\n\n// ---------- enable ----------\n\nconst yesIWroteItDownOption = Options.boolean(\"i-saved-the-passphrase\").pipe(\n Options.withDescription(\n \"Acknowledge that enable will print the org passphrase exactly once and that you will copy it somewhere safe. The passphrase is the only way to unlock the org's encryption vault on another machine; if lost, the only recovery is to re-enable encryption and re-onboard every device.\",\n ),\n);\n\nconst enableCommand = Command.make(\n \"enable\",\n { confirm: yesIWroteItDownOption, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const access = yield* VaultAccess;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n\n const existing = yield* access.fetchConfig;\n if (existing.enabled) {\n yield* out.error(\"encryption is already enabled for this org. Use `sp org encryption key rotate` to rotate the key.\");\n return yield* Effect.fail(new Aborted());\n }\n\n // Gate before generating anything: a run without the flag must not\n // reveal a passphrase, because each run generates a fresh one — a\n // passphrase printed here and \"saved\" would never match the vault a\n // later confirmed run pushes.\n if (!args.confirm) {\n yield* out.info(\"Enabling encryption generates an org passphrase that is shown exactly once.\");\n yield* out.info(\"It is the ONLY way to:\");\n yield* out.info(\" - unlock the encryption vault from another admin's machine\");\n yield* out.info(\" - recover access if your CLI state is lost\");\n yield* out.info(\"It cannot be recovered if forgotten. Re-enabling encryption forces\");\n yield* out.info(\"every member device to re-onboard from scratch.\");\n yield* out.info(\"\");\n yield* out.info(\"Re-run with --i-saved-the-passphrase to generate the passphrase and enable encryption.\");\n return yield* Effect.fail(new Aborted());\n }\n\n // Generate every secret locally; nothing in the vault touches the\n // backend in plaintext.\n const passphrase = yield* sodium.generatePassphrase();\n const vaultSalt = yield* sodium.generateVaultSalt;\n const adminKp = yield* sodium.generateAdminKeyPair;\n const masterKey = yield* sodium.generateMasterKey;\n\n const vaultContents: VaultContents = {\n adminPublicKey: adminKp.publicKey,\n adminPrivateKey: adminKp.privateKey,\n masterKeyCurrent: { version: 1, key: masterKey },\n masterKeyHistory: [],\n integrations: [],\n };\n\n const vaultKey = yield* sodium.deriveVaultKey(passphrase, vaultSalt, DEFAULT_KDF_PARAMS);\n const vaultBlob = yield* sodium.encryptVault(vaultContents, vaultKey);\n\n yield* out.info(\"\");\n yield* out.info(\"=== ORG ENCRYPTION PASSPHRASE — copy this now, it will not be shown again ===\");\n yield* out.info(\"\");\n yield* out.print(` ${passphrase}`);\n yield* out.info(\"\");\n yield* out.info(\"This passphrase is the ONLY way to:\");\n yield* out.info(\" - unlock the encryption vault from another admin's machine\");\n yield* out.info(\" - recover access if your CLI state is lost\");\n yield* out.info(\"It cannot be recovered if forgotten. Re-enabling encryption forces\");\n yield* out.info(\"every member device to re-onboard from scratch.\");\n yield* out.info(\"\");\n\n yield* api.post(\"enable\", \"/v1/org/encryption/enable\", {\n adminPubkeyB64: sodium.toB64(adminKp.publicKey),\n vaultBlobB64: sodium.toB64(vaultBlob),\n vaultSaltB64: sodium.toB64(vaultSalt),\n kdfParams: DEFAULT_KDF_PARAMS,\n });\n\n yield* vaultStore.save(vaultContents);\n yield* out.info(\"Encryption enabled. master_key version 1 generated.\");\n yield* out.info(\"Local vault cached at ~/.config/simplepush/vault.json (subsequent commands won't prompt).\");\n }),\n);\n\n// ---------- status ----------\n\nconst statusCommand = Command.make(\"status\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const access = yield* VaultAccess;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n\n const cfg = yield* access.fetchConfig;\n if (!cfg.enabled) {\n yield* out.print(\"Encryption: disabled\");\n return yield* out.info(\"Run `org encryption enable` to set it up.\");\n }\n\n yield* out.print(\"Encryption: enabled\");\n yield* out.print(`Admin pubkey: ${cfg.adminPubkeyB64 ?? \"?\"}`);\n\n // Read-only here: status shouldn't prompt for the passphrase just to\n // refresh the display. `sync` (and other commands that actually need the\n // vault contents) prompts on demand.\n const vault = Option.getOrUndefined(yield* vaultStore.load);\n // A cache whose admin pubkey doesn't match the server config is stale (a\n // different login's vault) — the next vault-using command clears it.\n const cacheStale =\n vault !== undefined &&\n typeof cfg.adminPubkeyB64 === \"string\" &&\n !sodium.constantTimeEqual(vault.adminPublicKey, sodium.fromB64(cfg.adminPubkeyB64));\n yield* out.print(\n `Vault cache: ${\n vault\n ? cacheStale\n ? \"STALE (doesn't match this org — next encryption command re-prompts)\"\n : `present (master_key v${vault.masterKeyCurrent.version})`\n : \"absent (next encryption command will prompt)\"\n }`,\n );\n\n const devices = yield* fetchEncryptionDevices;\n // A stale cache's key version says nothing about this org's devices.\n const currentVersion = cacheStale ? undefined : vault?.masterKeyCurrent.version;\n const upToDate = currentVersion === undefined\n ? 0\n : devices.filter((d) => d.wrappedKeys.some((w) => w.version === currentVersion)).length;\n const pending = devices.length - upToDate;\n yield* out.print(`Devices onboarded: ${devices.length}`);\n if (currentVersion !== undefined) {\n yield* out.print(` current key wrapped: ${upToDate}`);\n yield* out.print(` pending sync: ${pending}`);\n if (pending > 0) yield* out.info(\"Run `org encryption sync` to wrap the current master key to pending devices.\");\n } else {\n yield* out.info(\"Local vault cache is absent — `org encryption sync` will prompt for the passphrase.\");\n }\n }),\n);\n\n// ---------- shared wrap loop ----------\n\ninterface SyncCounts {\n readonly wrapped: number;\n readonly alreadyCurrent: number;\n readonly unverified: number;\n readonly failed: number;\n}\n\n// Walks every onboarded device in the org and wraps `master_key_current` to\n// any that don't already hold it. Verifies the device pubkey against the\n// CLI's locally-stored invite codes (HMAC match) before wrapping so a\n// malicious backend can't substitute a pubkey it controls. Returns counts\n// so callers (sync, key rotate) can print a summary in their own voice.\nconst wrapCurrentKeyToAllDevices = (vault: VaultContents) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n const invites = yield* InviteStore;\n const sodium = yield* Sodium;\n\n const devices = yield* fetchEncryptionDevices;\n const knownInvites = yield* invites.listValid;\n const currentMaster: MasterKey = vault.masterKeyCurrent;\n\n let counts: SyncCounts = { wrapped: 0, alreadyCurrent: 0, unverified: 0, failed: 0 };\n\n // Sequential on purpose: the wrap PUTs are cheap, and per-device error\n // reporting stays readable.\n yield* Effect.forEach(\n devices,\n (dev) =>\n Effect.gen(function* () {\n const pubkey = sodium.fromB64(dev.devicePubkeyB64);\n const storedHmac = sodium.fromB64(dev.inviteHmacB64);\n\n // A version match alone doesn't make a wrap current: versions\n // restart at 1 whenever an org enables encryption, so a wrap left\n // over from an earlier epoch collides with the current version.\n // crypto_box derives the same shared key in both directions, so the\n // admin can open its own wrap — only a blob that decrypts to the\n // current master key counts. This check runs before invite\n // verification so already-wrapped devices whose invite has been\n // consumed aren't reported as unverified.\n const currentWrap = dev.wrappedKeys.find((w) => w.version === currentMaster.version);\n const currentWrapValid =\n currentWrap !== undefined &&\n (yield* sodium.unwrapMasterKey(sodium.fromB64(currentWrap.blob), vault.adminPrivateKey, pubkey).pipe(\n Effect.map((key) => sodium.constantTimeEqual(key, currentMaster.key)),\n Effect.orElseSucceed(() => false),\n ));\n if (currentWrapValid) {\n counts = { ...counts, alreadyCurrent: counts.alreadyCurrent + 1 };\n return;\n }\n\n // Brute-force the local invite list — at typical org sizes (<<100\n // pending invites) this is negligible. Constant-time compare prevents\n // timing-side-channel leakage of which invite matched.\n const matchedInvite = knownInvites.find((inv) =>\n sodium.constantTimeEqual(sodium.hmacInviteBinding(inv.code, pubkey), storedHmac),\n );\n\n if (!matchedInvite) {\n counts = { ...counts, unverified: counts.unverified + 1 };\n return yield* out.error(\n `device ${dev.deviceId}: HMAC doesn't match any locally-known invite — skipping. ` +\n \"(This device joined via an invite issued from a different CLI install, or the invite has been pruned.)\",\n );\n }\n\n const blob = yield* sodium.wrapMasterKey(currentMaster.key, vault.adminPrivateKey, pubkey);\n // A stale current-version wrap means every wrap on the row predates\n // this epoch; replace the whole array instead of appending.\n const nextWraps: WrappedKey[] =\n currentWrap !== undefined\n ? [{ version: currentMaster.version, blob: sodium.toB64(blob) }]\n : [...dev.wrappedKeys, { version: currentMaster.version, blob: sodium.toB64(blob) }];\n yield* api.put(\"device wrap\", `/v1/org/encryption/devices/${encodeURIComponent(dev.deviceId)}/wraps`, {\n wraps: nextWraps,\n }).pipe(\n Effect.matchEffect({\n // A single failed PUT doesn't stop the walk — the summary (and a\n // non-zero exit from the caller) reports it.\n onFailure: (e) =>\n Effect.sync(() => {\n counts = { ...counts, failed: counts.failed + 1 };\n }).pipe(Effect.zipRight(out.error(`device wrap failed (${\"status\" in e ? e.status : \"?\"}): ${\"detail\" in e ? e.detail : String(e)}`))),\n onSuccess: () =>\n Effect.gen(function* () {\n counts = { ...counts, wrapped: counts.wrapped + 1 };\n // Local invite has served its purpose for this device; drop it\n // so the candidate set shrinks over time. The backend invite\n // row is consumed server-side by the redeem step (different\n // lifecycle).\n yield* invites.consume(matchedInvite.code).pipe(Effect.ignore);\n }),\n }),\n );\n }),\n { discard: true },\n );\n\n return counts;\n });\n\nconst summarize = (counts: SyncCounts) =>\n `wrapped=${counts.wrapped} already-current=${counts.alreadyCurrent} unverified=${counts.unverified}`;\n\n// ---------- sync ----------\n\nconst syncCommand = Command.make(\"sync\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const access = yield* VaultAccess;\n\n const vault = yield* access.getOrPrompt;\n const counts = yield* wrapCurrentKeyToAllDevices(vault);\n const integCounts = yield* wrapCurrentKeyToAllIntegrations(vault);\n\n yield* out.info(`Sync complete. Devices: ${summarize(counts)} Integrations: ${summarize(integCounts)}`);\n if (counts.unverified > 0) {\n yield* out.info(\"Unverified devices were left untouched — their pubkeys weren't bound to any invite code this CLI knows about.\");\n }\n if (counts.failed > 0 || integCounts.failed > 0) return yield* Effect.fail(new Aborted());\n }),\n);\n\n// Wraps `master_key_current` to every active integration whose pubkey matches\n// its VAULT PIN. The pin is the whole point: at rotation the backend hands us\n// a pubkey and we are about to wrap a fresh master key to it — an unpinned or\n// mismatched pubkey could be a backend substitution and is skipped loudly.\n// (Substitution at create time is harmless; the blobs were already sealed to\n// the real key. Rotation is the window the pin closes.)\nconst wrapCurrentKeyToAllIntegrations = (vault: VaultContents) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const sodium = yield* Sodium;\n\n const integrations = yield* fetchOrgIntegrations;\n const active = integrations.filter((i) => !i.revokedAt);\n const currentMaster: MasterKey = vault.masterKeyCurrent;\n\n let counts: SyncCounts = { wrapped: 0, alreadyCurrent: 0, unverified: 0, failed: 0 };\n\n yield* Effect.forEach(\n active,\n (integ) =>\n Effect.gen(function* () {\n const pin = vault.integrations.find((p) => p.id === integ.id);\n if (!pin) {\n counts = { ...counts, unverified: counts.unverified + 1 };\n return yield* out.error(\n `integration ${integ.id} ('${integ.name}') has no pubkey pin in the vault — skipping. ` +\n \"(Created before encryption was enabled, or its vault write failed. Revoke and re-create it.)\",\n );\n }\n if (pin.pubkeyB64 !== integ.pubkeyB64) {\n counts = { ...counts, unverified: counts.unverified + 1 };\n return yield* out.error(\n `integration ${integ.id} ('${integ.name}'): server pubkey DIFFERS from the vault pin — skipping. ` +\n \"This should never happen and may indicate backend tampering.\",\n );\n }\n if (integ.wrappedKeys.some((w) => w.version === currentMaster.version)) {\n counts = { ...counts, alreadyCurrent: counts.alreadyCurrent + 1 };\n return;\n }\n const blob = yield* sodium.wrapMasterKey(currentMaster.key, vault.adminPrivateKey, sodium.fromB64(pin.pubkeyB64));\n const nextWraps = [\n ...integ.wrappedKeys.filter((w) => w.version !== currentMaster.version),\n { version: currentMaster.version, blob: sodium.toB64(blob) },\n ];\n yield* putIntegrationWraps(integ.id, nextWraps).pipe(\n Effect.matchEffect({\n onFailure: (e) =>\n Effect.sync(() => {\n counts = { ...counts, failed: counts.failed + 1 };\n }).pipe(Effect.zipRight(out.error(`integration wrap failed: ${String(e)}`))),\n onSuccess: () =>\n Effect.sync(() => {\n counts = { ...counts, wrapped: counts.wrapped + 1 };\n }),\n }),\n );\n }),\n { discard: true },\n );\n\n return counts;\n });\n\n// ---------- key show ----------\n\nconst keyShowCommand = Command.make(\"show\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const access = yield* VaultAccess;\n const sodium = yield* Sodium;\n\n const vault = yield* access.getOrPrompt;\n yield* out.info(`Current encryption key (master_key v${vault.masterKeyCurrent.version}):`);\n yield* out.print(sodium.toB64(vault.masterKeyCurrent.key));\n yield* out.info(\"Use this value plus the org API key when configuring library clients.\");\n yield* out.info(\"If it ever leaks, run `sp org encryption key rotate` to invalidate it.\");\n }),\n);\n\n// ---------- key rotate ----------\n\nconst keyRotateCommand = Command.make(\"rotate\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const access = yield* VaultAccess;\n const vaultStore = yield* VaultStore;\n const sodium = yield* Sodium;\n\n // Re-prompts even if the local cache is present: rotation needs\n // `vault_key` itself to re-encrypt the updated vault, and the cache\n // deliberately doesn't store it (see VaultAccess.unlockForRotation).\n const { vault, vaultKey } = yield* access.unlockForRotation;\n\n // Build the next vault contents: new random master_key as current,\n // previous current pushed into history. We don't change the admin keypair\n // here — rotation of `master_key` is the common case (leak response,\n // periodic), while admin-keypair rotation is a separate, much more\n // expensive flow (every device has to re-pin).\n const nextVersion = vault.masterKeyCurrent.version + 1;\n const nextVault: VaultContents = {\n adminPublicKey: vault.adminPublicKey,\n adminPrivateKey: vault.adminPrivateKey,\n masterKeyCurrent: { version: nextVersion, key: yield* sodium.generateMasterKey },\n masterKeyHistory: [...vault.masterKeyHistory, vault.masterKeyCurrent],\n // Pins survive rotation untouched — the pubkeys don't change, the\n // wrapped material does.\n integrations: vault.integrations,\n };\n\n // Re-fetch salt + params from the server (rather than re-deriving) so the\n // new vault blob lines up exactly with what the server will hand out to\n // unlock callers next time.\n const cfg = yield* access.fetchConfig.pipe(Effect.flatMap(access.requireEnabled));\n\n const newBlob = yield* sodium.encryptVault(nextVault, vaultKey);\n yield* api.put(\"vault update\", \"/v1/org/encryption/vault\", {\n vaultBlobB64: sodium.toB64(newBlob),\n vaultSaltB64: cfg.vaultSaltB64,\n kdfParams: cfg.kdfParams,\n });\n\n // Update the local cache before wrapping — if the wrap step fails partway,\n // the user can re-run `sync` to catch up, and we still want subsequent\n // `notify` calls on this machine to use the new key.\n yield* vaultStore.save(nextVault);\n\n yield* out.info(`Generated master_key v${nextVersion} and updated the org vault.`);\n yield* out.info(\"Wrapping the new key to every onboarded device...\");\n const counts = yield* wrapCurrentKeyToAllDevices(nextVault);\n yield* out.info(\"Wrapping the new key to every integration...\");\n const integCounts = yield* wrapCurrentKeyToAllIntegrations(nextVault);\n\n yield* out.info(`Rotation complete. Devices: ${summarize(counts)} Integrations: ${summarize(integCounts)}`);\n yield* out.info(\"\");\n yield* out.info(`New encryption key (master_key v${nextVersion}):`);\n yield* out.print(sodium.toB64(nextVault.masterKeyCurrent.key));\n yield* out.info(\"Update any library clients with this new value. The previous key remains valid for decrypting historical notifications only.\");\n if (counts.unverified > 0) {\n yield* out.info(\"Unverified devices were skipped — they'll need a fresh invite redeem before they can pick up the new key.\");\n }\n if (counts.failed > 0 || integCounts.failed > 0) return yield* Effect.fail(new Aborted());\n }),\n);\n\nconst keyCommand = Command.make(\"key\").pipe(\n Command.withSubcommands([keyShowCommand, keyRotateCommand]),\n);\n\n// ---------- root ----------\n\nexport const encryptionCommand = Command.make(\"encryption\").pipe(\n Command.withSubcommands([enableCommand, statusCommand, syncCommand, keyCommand]),\n);\n","// `sp org` — organization management. Subcommands authenticate with the CLI session\n// token saved by `sp auth login` (~/.config/simplepush/auth.json), via the Api\n// service; every wire shape is Schema-validated at the boundary.\n\nimport { Args, Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Schema } from \"effect\";\n\nimport { quietOption } from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { InviteStore } from \"../services/stores.js\";\nimport { Sodium } from \"../crypto/sodium.js\";\nimport { hashInviteCode } from \"../crypto/index.js\";\nimport { formatInstant } from \"../format.js\";\nimport { encryptionCommand } from \"./org-encryption.js\";\n\n// Optional backend fields (scala Option) are ABSENT from the JSON when unset —\n// zio-json omits None on encode, it never sends `\"field\": null`.\nconst optionalString = Schema.optional(Schema.NullOr(Schema.String));\n\nconst InviteResponse = Schema.Struct({\n role: Schema.String,\n expiresAt: Schema.String,\n seatsUsed: Schema.Number,\n seatsTotal: Schema.Number,\n});\n\nconst InviteSummary = Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n email: optionalString,\n role: Schema.String,\n expiresAt: Schema.String,\n createdAt: Schema.String,\n});\n\nconst MemberSummary = Schema.Struct({\n id: Schema.String,\n name: optionalString,\n email: optionalString,\n createdAt: Schema.String,\n});\n\nconst MembersResponse = Schema.Struct({ members: Schema.Array(MemberSummary) });\nconst InvitesResponse = Schema.Struct({ invites: Schema.Array(InviteSummary) });\n\nconst ApiKeyInfoResponse = Schema.Struct({\n prefix: Schema.String,\n createdAt: Schema.String,\n lastRotatedAt: optionalString,\n});\n\nconst RotateApiKeyResponse = Schema.Struct({\n apiKey: Schema.String,\n prefix: Schema.String,\n createdAt: Schema.String,\n lastRotatedAt: optionalString,\n});\n\nconst OrgTopicSummary = Schema.Struct({\n id: Schema.String,\n value: Schema.String,\n createdAt: Schema.String,\n});\n\nconst ListOrgTopicsResponse = Schema.Struct({ topics: Schema.Array(OrgTopicSummary) });\n\nconst ListOrgTopicMembersResponse = Schema.Struct({\n members: Schema.Array(Schema.Struct({ id: Schema.String, name: optionalString, email: optionalString })),\n});\n\n// ---------- commands ----------\n\nconst nameArg = Args.text({ name: \"name\" }).pipe(\n Args.withDescription(\"Display name of the person being invited (used to address them).\"),\n);\n\nconst idArg = Args.text({ name: \"id\" }).pipe(\n Args.withDescription(\"Resource id (UUID) — copy from the matching `list` output.\"),\n);\n\nconst memberNameArg = Args.text({ name: \"name\" }).pipe(\n Args.withDescription(\"Display name of the member to remove (case-insensitive). Per-org names are unique.\"),\n);\n\nconst roleOption = Options.choice(\"role\", [\"member\", \"admin\"] as const).pipe(\n Options.withDescription(\"Role for the invitee. Members consume a seat; admins do not.\"),\n Options.withDefault(\"member\" as const),\n);\n\nconst emailOption = Options.text(\"email\").pipe(\n Options.withDescription(\"Optional contact email. Saved on the invite and propagated to user.email on redemption. Not used for sending — just a label.\"),\n Options.optional,\n);\n\nconst inviteCommand = Command.make(\n \"invite\",\n { name: nameArg, role: roleOption, email: emailOption, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const invites = yield* InviteStore;\n const sodium = yield* Sodium;\n\n const email = Option.getOrUndefined(args.email);\n // Generate the cleartext code locally — the backend only ever sees the\n // hash, so a compromised backend can't forge HMACs for a substituted\n // device pubkey at sync time. The cleartext lives only in\n // `~/.config/simplepush/invites.json` (and, briefly, in the member's app\n // at redemption).\n const code = yield* sodium.generateInviteCode;\n const codeHash = hashInviteCode(code);\n const body: Record<string, string> = { name: args.name, role: args.role, codeHash };\n if (email !== undefined) body.email = email;\n\n const payload = yield* api.postJson(\"invite\", \"/v1/org/members/invites\", InviteResponse, body);\n\n // Persist the code locally so a later `org encryption sync` can verify\n // each device's HMAC(code, pubkey) against the actual issued code. The\n // backend only stores hash(code), so without this the CLI loses the\n // ability to authenticate device pubkeys at wrap time. We persist for\n // all invites — even if encryption isn't enabled yet, it might be later,\n // and the entry harmlessly ages out at expiresAt.\n yield* invites\n .append({\n code,\n name: args.name,\n role: args.role,\n issuedAt: new Date().toISOString(),\n expiresAt: payload.expiresAt,\n })\n .pipe(\n // Don't fail the command for a local-storage hiccup — the backend\n // already created the invite, and the code below is the user-visible\n // contract. Just warn so they know encryption sync would miss this\n // one until it's re-issued.\n Effect.catchAll((err) =>\n out.error(`(warning) failed to persist invite locally for encryption sync: ${err instanceof Error ? err.message : String(err)}`),\n ),\n );\n\n yield* out.info(`Invite created for ${args.name}${email ? ` <${email}>` : \"\"} (role: ${payload.role}).`);\n yield* out.info(`Expires: ${formatInstant(payload.expiresAt)}`);\n if (payload.role === \"member\") yield* out.info(`Seats used: ${payload.seatsUsed} / ${payload.seatsTotal}`);\n // The code is shown exactly once — print it on stdout so callers can pipe it.\n yield* out.print(`Login code: ${code}`);\n }),\n);\n\nconst membersListCommand = Command.make(\"list\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const { members } = yield* api.getJson(\"list\", \"/v1/org/members\", MembersResponse);\n if (members.length === 0) return yield* out.info(\"no active members\");\n yield* Effect.forEach(members, (m) =>\n out.print(`${m.name ?? \"(unnamed)\"}\\t${m.email ?? \"-\"}\\t${formatInstant(m.createdAt)}`),\n );\n }),\n);\n\n// `--yes` gates the destructive call. Without it the CLI prints a warning of\n// exactly what gets deleted server-side and exits non-zero without touching\n// the backend, so accidental `sp org members remove alice` (e.g. tab completion\n// nudging the wrong name) doesn't wipe data. Scripts pass `--yes` to confirm.\nconst yesOption = Options.boolean(\"yes\").pipe(\n Options.withAlias(\"y\"),\n Options.withDescription(\"Confirm the removal. Without this flag the command only prints a warning and exits.\"),\n);\n\n// Resolve name → uuid via the list endpoint. The backend keys deletes on UUID\n// (stable across renames), but per-org names are case-insensitively unique by\n// DB constraint so a name lookup is unambiguous. Two requests per op is\n// acceptable for a human-driven CLI.\nconst resolveMemberByName = (name: string) =>\n Effect.gen(function* () {\n const api = yield* Api;\n const { members } = yield* api.getJson(\"resolve member\", \"/v1/org/members\", MembersResponse);\n const target = name.trim().toLowerCase();\n const match = members.find((m) => (m.name ?? \"\").toLowerCase() === target);\n if (!match) {\n return yield* Effect.fail(\n new UserError({ message: `no member named '${name}' — run \\`simplepush org members list\\` to see members` }),\n );\n }\n return match;\n });\n\nconst membersRemoveCommand = Command.make(\n \"remove\",\n { name: memberNameArg, yes: yesOption, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const match = yield* resolveMemberByName(args.name).pipe(\n Effect.mapError((e) => (e._tag === \"UserError\" ? new UserError({ message: `no member named '${args.name}' in this org` }) : e)),\n );\n\n yield* out.info(`Removing '${match.name ?? args.name}' permanently deletes all of their associated data. Only event history is retained.`);\n if (!args.yes) {\n yield* out.info(\"Re-run with --yes (or -y) to confirm.\");\n return yield* Effect.fail(new Aborted());\n }\n\n yield* api.delete(\"remove\", `/v1/org/members/${encodeURIComponent(match.id)}`);\n yield* out.info(`removed member ${match.name ?? args.name}`);\n }),\n);\n\nconst invitesListCommand = Command.make(\"list\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const { invites } = yield* api.getJson(\"list\", \"/v1/org/members/invites\", InvitesResponse);\n if (invites.length === 0) return yield* out.info(\"no pending invites\");\n yield* Effect.forEach(invites, (inv) =>\n out.print(`${inv.id}\\t${inv.name}\\t${inv.email ?? \"-\"}\\t${inv.role}\\texpires ${formatInstant(inv.expiresAt)}`),\n );\n }),\n);\n\nconst invitesRevokeCommand = Command.make(\"revoke\", { id: idArg, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n yield* api.delete(\"revoke\", `/v1/org/members/invites/${encodeURIComponent(args.id)}`);\n yield* out.info(`revoked invite ${args.id}`);\n }),\n);\n\n// `members` carries: invite, list, remove. The shorter `sp org members` -> list is\n// usually what people type; we pull the `members list` subcommand alongside it for\n// explicit usage too.\nconst membersCommand = Command.make(\"members\").pipe(\n Command.withSubcommands([inviteCommand, membersListCommand, membersRemoveCommand]),\n);\n\n// `invites` carries: list, revoke. Mirrors GET / DELETE on /v1/org/members/invites.\nconst invitesCommand = Command.make(\"invites\").pipe(\n Command.withSubcommands([invitesListCommand, invitesRevokeCommand]),\n);\n\n// ---------- api-key ----------\n\nconst apiKeyInfoCommand = Command.make(\"info\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const payload = yield* api.getJson(\"info\", \"/v1/org/api-key\", ApiKeyInfoResponse);\n yield* out.print(`Prefix: ${payload.prefix}…`);\n yield* out.print(`Created: ${formatInstant(payload.createdAt)}`);\n yield* out.print(`Last rotated: ${payload.lastRotatedAt ? formatInstant(payload.lastRotatedAt) : \"never\"}`);\n yield* out.info(\"(plaintext is unrecoverable; run `api-key rotate` to surface a new key)\");\n }),\n);\n\nconst apiKeyRotateCommand = Command.make(\"rotate\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n\n const payload = yield* api.postJson(\"rotate\", \"/v1/org/api-key/rotate\", RotateApiKeyResponse);\n yield* out.info(\"The previous key (if any) is now invalid.\");\n yield* out.print(\"\\nOrganization API key (shown once, copy now):\");\n yield* out.print(` ${payload.apiKey}`);\n }),\n);\n\nconst apiKeyCommand = Command.make(\"api-key\").pipe(\n Command.withSubcommands([apiKeyInfoCommand, apiKeyRotateCommand]),\n);\n\n// ---------- topics ----------\n//\n// Org topics are admin-managed channels: admins create them, assign members to them,\n// and then send notifications addressed to the topic via the API key. Members do not\n// self-subscribe — assignment is one-way from the admin side, mirroring the existing\n// invite flow.\n\nconst topicValueArg = Args.text({ name: \"value\" }).pipe(\n Args.withDescription(\"Topic value (no whitespace, ≤ 255 chars). Case-insensitive uniqueness within the org.\"),\n);\n\n// We resolve topic by `value` everywhere on the CLI because admins type names\n// they recognize. The HTTP API itself keys on the topic id (stable across\n// renames if we ever add renames).\nconst resolveOrgTopicIdByValue = (value: string) =>\n Effect.gen(function* () {\n const api = yield* Api;\n const { topics } = yield* api.getJson(\"list org topics\", \"/v1/org/topics\", ListOrgTopicsResponse);\n const target = value.trim().toLowerCase();\n const match = topics.find((t) => t.value.toLowerCase() === target);\n if (!match) {\n return yield* Effect.fail(\n new UserError({ message: `no org topic '${value}' — run \\`simplepush org topics list\\` to see available topics` }),\n );\n }\n return match.id;\n });\n\nconst topicsCreateCommand = Command.make(\"create\", { value: topicValueArg, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const payload = yield* api.postJson(\"create\", \"/v1/org/topics\", OrgTopicSummary, { value: args.value });\n yield* out.info(`Created org topic '${payload.value}'.`);\n }),\n);\n\nconst topicsListCommand = Command.make(\"list\", { quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const { topics } = yield* api.getJson(\"list\", \"/v1/org/topics\", ListOrgTopicsResponse);\n if (topics.length === 0) return yield* out.info(\"no org topics\");\n yield* Effect.forEach(topics, (t) => out.print(`${t.value}\\tcreated ${formatInstant(t.createdAt)}`));\n }),\n);\n\nconst topicsDeleteCommand = Command.make(\"delete\", { value: topicValueArg, quiet: quietOption }, (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.value);\n yield* api.delete(\"delete\", `/v1/org/topics/${encodeURIComponent(orgTopicId)}`);\n yield* out.info(`Deleted org topic '${args.value}'.`);\n }),\n);\n\nconst topicValueAssignArg = Args.text({ name: \"topic\" }).pipe(\n Args.withDescription(\"Org topic value (from `topics list`).\"),\n);\n\nconst memberNameAssignArg = Args.text({ name: \"member\" }).pipe(\n Args.withDescription(\"Member display name (case-insensitive, from `members list`).\"),\n);\n\nconst topicsAssignCommand = Command.make(\n \"assign\",\n { topic: topicValueAssignArg, member: memberNameAssignArg, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.topic);\n const member = yield* resolveMemberByName(args.member);\n yield* api.put(\"assign\", `/v1/org/topics/${encodeURIComponent(orgTopicId)}/members/${encodeURIComponent(member.id)}`);\n yield* out.info(`Assigned ${args.member} to org topic '${args.topic}'.`);\n }),\n);\n\nconst topicsUnassignCommand = Command.make(\n \"unassign\",\n { topic: topicValueAssignArg, member: memberNameAssignArg, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.topic);\n const member = yield* resolveMemberByName(args.member);\n yield* api.delete(\"unassign\", `/v1/org/topics/${encodeURIComponent(orgTopicId)}/members/${encodeURIComponent(member.id)}`);\n yield* out.info(`Unassigned ${args.member} from org topic '${args.topic}'.`);\n }),\n);\n\nconst topicsMembersCommand = Command.make(\n \"members\",\n { topic: topicValueAssignArg, quiet: quietOption },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n const api = yield* Api;\n const orgTopicId = yield* resolveOrgTopicIdByValue(args.topic);\n const { members } = yield* api.getJson(\n \"members\",\n `/v1/org/topics/${encodeURIComponent(orgTopicId)}/members`,\n ListOrgTopicMembersResponse,\n );\n if (members.length === 0) return yield* out.info(\"no members assigned\");\n yield* Effect.forEach(members, (m) => out.print(`${m.name ?? \"(unnamed)\"}\\t${m.email ?? \"-\"}`));\n }),\n);\n\nconst topicsCommand = Command.make(\"topics\").pipe(\n Command.withSubcommands([\n topicsCreateCommand,\n topicsListCommand,\n topicsDeleteCommand,\n topicsAssignCommand,\n topicsUnassignCommand,\n topicsMembersCommand,\n ]),\n);\n\nexport const orgCommand = Command.make(\"org\").pipe(\n Command.withSubcommands([membersCommand, invitesCommand, apiKeyCommand, topicsCommand, encryptionCommand]),\n);\n","import { basename, extname } from \"node:path\";\n\nimport { FileSystem } from \"@effect/platform\";\nimport { Effect } from \"effect\";\nimport { type FileAttachment } from \"@simplepush/sdk\";\n\n/** Read each `--file` path into a `FileAttachment` the SDK can upload: bytes +\n * basename + a best-effort content type guessed from the extension (the SDK\n * defaults to application/octet-stream when undefined). */\nexport const buildFiles = (paths: ReadonlyArray<string>) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n return yield* Effect.forEach(paths, (p) =>\n Effect.map(fs.readFile(p), (data): FileAttachment => {\n const contentType = guessContentType(p);\n return {\n filename: basename(p),\n data,\n ...(contentType !== undefined ? { contentType } : {}),\n };\n }),\n );\n });\n\n// Minimal extension -> MIME map. The content type drives receiver-side\n// rendering (image/video inline vs a generic file), so cover the common media\n// kinds; anything else falls through to the SDK's octet-stream default.\nconst CONTENT_TYPES: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n heic: \"image/heic\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n txt: \"text/plain\",\n json: \"application/json\",\n csv: \"text/csv\",\n zip: \"application/zip\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n mp3: \"audio/mpeg\",\n m4a: \"audio/mp4\",\n wav: \"audio/wav\",\n};\n\nfunction guessContentType(path: string): string | undefined {\n const ext = extname(path).slice(1).toLowerCase();\n return CONTENT_TYPES[ext];\n}\n","import { Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Stream } from \"effect\";\nimport {\n type Input,\n type SendOptions,\n type OrgSendTarget,\n type Task,\n type Upload,\n} from \"@simplepush/sdk\";\n\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n requireApiToken,\n topicOption,\n willEncrypt,\n type PasswordFlag,\n} from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { acquireClient, acquireOrgClient, sdkCall, sdkStream } from \"../services/sdk.js\";\nimport { bearerToken } from \"../services/stores.js\";\nimport { formatSent } from \"../collect-output.js\";\nimport { resolveExpiresAt } from \"../since.js\";\nimport { buildFiles } from \"../files.js\";\nimport { buildInputs } from \"../input-spec.js\";\n\nconst titleOption = Options.text(\"title\").pipe(\n Options.withDescription(\"Task title.\"),\n Options.optional,\n);\n\nconst contentOption = Options.text(\"content\").pipe(\n Options.withDescription(\"Task description / body content.\"),\n Options.optional,\n);\n\nconst tagOption = Options.text(\"tag\").pipe(\n Options.withDescription(\"Tag the task for receiver-side filtering. Defaults to $SP_TAG.\"),\n Options.optional,\n);\n\nconst repeatedText = (name: string, alias: string | undefined, description: string) => {\n const base = Options.text(name).pipe(\n Options.withDescription(description),\n Options.repeated,\n );\n return alias ? Options.withAlias(alias)(base) : base;\n};\n\nconst textInput = repeatedText(\n \"text-input\",\n undefined,\n \"Add a text input. Format: `description[;key=value...]`. Settings: `required=true|false` (default true), `defaultValue=...`. Repeatable.\",\n);\nconst choiceInput = repeatedText(\n \"choice-input\",\n \"c\",\n \"Add a choice input. Format: `[description;]options[;key=value...]`, options comma-separated. Settings: `required=true|false`, `multi=true|false` (allow picking more than one option, default false), `minSelections=<int>`/`maxSelections=<int>` (only with multi). Use `\\\\;` for a literal semicolon. Repeatable.\",\n);\nconst actionInput = repeatedText(\n \"action-input\",\n \"a\",\n \"Add an actions input (buttons the recipient taps, e.g. Accept/Deny). Format: `[description;]key=Label[:style],...[;required=true|false]`, actions comma-separated; style is default|primary|destructive. Use `\\\\,` for a literal comma in a label. Repeatable.\",\n);\nconst sliderInput = repeatedText(\n \"slider-input\",\n \"s\",\n \"Add a slider input (the recipient picks a number on a scale). Format: `[description;]min=0;max=14;step=0.1;unit=pH;default=7`. `min`/`max` are required; `step`/`unit`/`default` optional. Repeatable.\",\n);\nconst photoInput = repeatedText(\n \"photo-input\",\n undefined,\n \"Add a photo input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\nconst voiceRecordingInput = repeatedText(\n \"voice-recording-input\",\n undefined,\n \"Add a voice recording input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\nconst fileInput = repeatedText(\n \"file-input\",\n undefined,\n \"Add a file upload input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\nconst locationInput = repeatedText(\n \"location-input\",\n undefined,\n \"Add a location input (the recipient shares their device GPS position from the app). Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n);\n\nconst linkOption = repeatedText(\n \"link\",\n \"l\",\n \"Attach a remote URL (a link attachment). Repeatable. For local files use --file.\",\n);\n\nconst fileOption = repeatedText(\n \"file\",\n \"f\",\n \"Attach a local file, uploaded as a file attachment (encrypted under the send's key — topic password or org master key — when the send is encrypted). Repeatable.\",\n);\n\nconst submitOption = Options.boolean(\"submit\").pipe(\n Options.withDescription(\"Require the recipient to explicitly submit the task. Without this, it auto-completes once the required inputs are filled.\"),\n);\n\nconst waitOption = Options.boolean(\"wait\").pipe(\n Options.withDescription(\"Block until the task is completed; print the result to stdout. Requires exactly one input on the request.\"),\n);\n\n// `--reply <mode>` opts recipients into the composer below the rendered\n// task. Wire values match the backend's ReplyMode strings. Absent =\n// no composer (default).\nconst replyOption = Options.choice(\"reply\", [\"one-shot\", \"sticky\", \"one-time-per-user\"] as const).pipe(\n Options.withDescription(\n \"Show a reply composer on the recipient's task: 'one-shot' (first reply wins, closes the slot), 'sticky' (open indefinitely), 'one-time-per-user' (one reply per user).\",\n ),\n Options.optional,\n);\n\n// Org targeting (mutually exclusive with each other and with -k/--topic, the\n// personal-topic flag). When any is set the task goes out via the CLI bearer\n// session through the SDK's OrgClient (org-vault encryption), like `sp notify`.\nconst memberOption = Options.text(\"member\").pipe(\n Options.withAlias(\"m\"),\n Options.withDescription(\"Send to a single org member by display name (case-insensitive). Org send; mutually exclusive with --broadcast, --org-topic, and -k/--topic.\"),\n Options.optional,\n);\n\nconst broadcastOption = Options.boolean(\"broadcast\").pipe(\n Options.withAlias(\"b\"),\n Options.withDescription(\"Send to every member of the org. Org send; mutually exclusive with --member, --org-topic, and -k/--topic.\"),\n);\n\nconst orgTopicOption = Options.text(\"org-topic\").pipe(\n Options.withAlias(\"o\"),\n Options.withDescription(\"Send to an org topic by value (from `sp org topics list`). Org send; mutually exclusive with --member, --broadcast, and -k/--topic (which is the personal topic).\"),\n Options.optional,\n);\n\nconst noEncryptOption = Options.boolean(\"no-encrypt\").pipe(\n Options.withDescription(\"For org sends: send fields in plaintext even when the org vault is unlocked.\"),\n);\n\nconst markdownOption = Options.boolean(\"markdown\").pipe(\n Options.withDescription(\"Render the task body as Markdown on the recipient's device (sets contentFormat=markdown).\"),\n);\n\nconst expiresOption = Options.text(\"expires\").pipe(\n Options.withDescription(\n \"Deadline: a duration from now (`2h`, `7d`) or an ISO 8601 timestamp. Past it, an unanswered task flips to expired (terminal; further answers are rejected).\",\n ),\n Options.optional,\n);\n\nconst sharedOption = Options.boolean(\"shared\").pipe(\n Options.withDescription(\n \"Shared mode: ONE task all recipients see and answer together (user A's input is visible to user B). Default (without this flag) is independent mode: every recipient gets their own task instance under a group.\",\n ),\n);\n\n// stdout shape for a send (not `--wait`): `text` prints the bare id (default,\n// human/script), `json` prints a machine-readable `sent` line that\n// `sp collect` consumes to know the group + members + resume point.\nconst formatOption = Options.choice(\"format\", [\"text\", \"json\"] as const).pipe(\n Options.withDescription(\"stdout format for a send: `text` (the bare id, default) or `json` (a `sent` line piped to `sp collect`).\"),\n Options.withDefault(\"text\"),\n);\n\nexport const taskCommand = Command.make(\n \"task\",\n {\n title: titleOption,\n content: contentOption,\n tag: tagOption,\n \"text-input\": textInput,\n \"choice-input\": choiceInput,\n \"action-input\": actionInput,\n \"slider-input\": sliderInput,\n \"photo-input\": photoInput,\n \"voice-recording-input\": voiceRecordingInput,\n \"file-input\": fileInput,\n \"location-input\": locationInput,\n link: linkOption,\n file: fileOption,\n submit: submitOption,\n wait: waitOption,\n reply: replyOption,\n member: memberOption,\n broadcast: broadcastOption,\n \"org-topic\": orgTopicOption,\n \"no-encrypt\": noEncryptOption,\n markdown: markdownOption,\n shared: sharedOption,\n expires: expiresOption,\n format: formatOption,\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const memberName = Option.getOrUndefined(args.member);\n const orgTopicName = Option.getOrUndefined(args[\"org-topic\"]);\n const topic = args.topic[0];\n // At most one of: org member, org broadcast, org topic, or a personal topic.\n // ZERO targets on a personal send = a note-to-self (your own devices).\n const targetCount = [memberName !== undefined, args.broadcast, orgTopicName !== undefined, topic !== undefined].filter(Boolean).length;\n if (targetCount > 1) {\n return yield* Effect.fail(\n new UserError({ message: \"pass at most one target: -m <member> | -b (broadcast) | --org-topic <value> | -t <topic> (omit all for a self-send)\" }),\n );\n }\n\n // Shared task content (plaintext here; the org path encrypts under the vault).\n const inputs = yield* Effect.try({\n try: () => buildInputs(args),\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n if (inputs.length === 0 && args.wait) {\n yield* out.warn(\"--wait requested but no inputs were defined; the server will never produce a TaskCompleted event\");\n }\n const tag = Option.getOrElse(args.tag, () => process.env.SP_TAG ?? \"\");\n const title = Option.getOrUndefined(args.title);\n const content = Option.getOrUndefined(args.content);\n const expiresRaw = Option.getOrUndefined(args.expires);\n const expiresAt = expiresRaw !== undefined\n ? yield* Effect.try({\n try: () => resolveExpiresAt(expiresRaw),\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n })\n : undefined;\n\n // Org send (member / broadcast / org topic): CLI bearer session + org-vault\n // field encryption, POST to /v1/org/tasks/json. Mirrors `sp notify`.\n if (memberName !== undefined || args.broadcast || orgTopicName !== undefined) {\n return yield* sendOrgTask({\n member: memberName,\n broadcast: args.broadcast,\n orgTopic: orgTopicName,\n tag,\n title,\n content,\n inputs,\n links: [...args.link],\n files: [...args.file],\n autoCommit: !args.submit,\n reply: Option.getOrUndefined(args.reply),\n markdown: args.markdown,\n noEncrypt: args[\"no-encrypt\"],\n wait: args.wait,\n shared: args.shared,\n expiresAt,\n format: args.format,\n });\n }\n\n // Personal send: a topic (password@topic path) OR a note-to-self (no topic).\n const passwords = args.password as ReadonlyArray<PasswordFlag>;\n // The SDK auto-encrypts a topic send when a `password@topic` pair matches\n // the topic; a note-to-self encrypts under the account default password (a\n // bare --password). Warn about the Argon2 cost in either case.\n const encrypting = willEncrypt(passwords, topic);\n\n // Read each --file off disk into a FileAttachment; the SDK encrypts the\n // bytes (when the topic has a password) and drives the upload lifecycle.\n const files = yield* buildFiles(args.file);\n\n // A client always carries a credential (matching the Python SDK) — even\n // though topic sends themselves go out without it on the wire.\n const apiToken = yield* requireApiToken(args[\"api-token\"]);\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireClient({\n baseUrl: args[\"base-url\"],\n apiToken,\n passwords: [...passwords],\n });\n\n // Encryption is driven by the configured `password@topic` pair for this\n // topic; the SDK encrypts each field under the topic key (salt = topic value).\n if (encrypting) yield* out.info(\"encrypting outgoing task (Argon2id, this takes a moment)\");\n const baseOpts = {\n ...(tag ? { tag } : {}),\n ...(title !== undefined ? { title } : {}),\n ...(content !== undefined ? { content } : {}),\n inputs,\n links: [...args.link],\n ...(files.length > 0 ? { files } : {}),\n autoCommit: !args.submit,\n ...(Option.isSome(args.reply) ? { reply: args.reply.value } : {}),\n ...(args.markdown ? { contentFormat: \"markdown\" as const } : {}),\n ...(expiresAt !== undefined ? { expiresAt } : {}),\n };\n\n if (topic === undefined) {\n // Note to self: no topic → the task goes to your own devices, returned\n // as a single Task (never a group — there is one recipient, you).\n const response = yield* sdkCall(\"task send\", () => client.sendTask(baseOpts));\n yield* out.info(`self-send task created: ${response.taskId}`);\n yield* out.info(`append token: ${response.appendToken}`);\n if (!args.wait) {\n if (args.format === \"json\") yield* out.print(formatSent(undefined, response.createdAt, [{ id: response.taskId, kind: \"task\", recipient: null }]));\n else yield* out.print(response.taskId);\n return;\n }\n yield* out.info(`waiting for completion of task ${response.taskId}`);\n return yield* waitForFirstCompletion([response]);\n }\n\n const sendOpts = { ...baseOpts, topic };\n\n if (args.shared) {\n // Single shared task: one id/token, all recipients share state.\n const response = yield* sdkCall(\"task send\", () => client.sendTask({ ...sendOpts, shared: true }));\n yield* out.info(`task created: ${response.taskId}`);\n yield* out.info(`append token: ${response.appendToken}`);\n if (!args.wait) {\n if (args.format === \"json\") yield* out.print(formatSent(undefined, response.createdAt, [{ id: response.taskId, kind: \"task\", recipient: null }]));\n else yield* out.print(response.taskId);\n return;\n }\n yield* out.info(`waiting for completion of task ${response.taskId}`);\n return yield* waitForFirstCompletion([response]);\n }\n\n // Independent mode (default): one task instance per recipient under a group.\n const group = yield* sdkCall(\"task send\", () => client.sendTask(sendOpts));\n yield* out.info(`task group created: ${group.groupId} (${group.instances.length} recipient${group.instances.length === 1 ? \"\" : \"s\"})`);\n yield* out.info(`group append token: ${group.appendToken}`);\n yield* Effect.forEach(group.instances, (inst) => {\n const who = inst.recipient ? `${inst.recipient.publicId}${inst.recipient.name ? ` (${inst.recipient.name})` : \"\"}` : \"unknown\";\n return out.info(`instance: ${inst.taskId} -> ${who} append token: ${inst.appendToken}`);\n });\n if (group.instances.length === 0) yield* out.warn(\"the topic has no recipients — the group is empty\");\n\n if (!args.wait) {\n if (args.format === \"json\") {\n // Machine-readable handle for `sp collect`: the group + its members\n // (taskId + recipient) + createdAt (the collect resume point).\n yield* out.print(\n formatSent(\n group.groupId,\n group.createdAt,\n group.instances.map((i) => ({\n id: i.taskId,\n kind: \"task\" as const,\n recipient: i.recipient ? { publicId: i.recipient.publicId, name: i.recipient.name ?? null } : null,\n })),\n ),\n );\n } else {\n // stdout contract: the primary handle. grptsk_-prefixed, so scripts\n // can tell it apart from a plain tsk_ id.\n yield* out.print(group.groupId);\n }\n return;\n }\n\n if (group.instances.length === 0) {\n // --wait promises a completion value on stdout; with zero instances\n // none can ever arrive, so this is a failure, not a quiet success.\n yield* out.warn(\"--wait requested but the group has no instances; nothing will complete\");\n return yield* Effect.fail(new Aborted());\n }\n yield* out.info(`waiting for the first completion across ${group.instances.length} instance(s) of ${group.groupId}`);\n yield* waitForFirstCompletion(group.instances);\n }),\n );\n }),\n);\n\n/** Merge every instance's input stream and take the FIRST completed answer\n * (mirrors the backend's curl Wait semantics: one answer from any recipient).\n * Per instance, a `taskDeleted` benignly ends that sub-stream. No completion\n * anywhere is a FAILURE (exit 1, empty stdout); a real stream error (auth,\n * transport) fails the merge and is surfaced as such. */\nconst waitForFirstCompletion = (tasks: readonly Task[]) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n\n const completions = tasks.map((t) =>\n sdkStream(\"task wait stream\", (signal) => t.inputs({ replay: true, signal })).pipe(\n Stream.takeWhile((ev) => ev.kind !== \"taskDeleted\"),\n Stream.filter((ev) => ev.kind === \"taskCompleted\"),\n Stream.take(1),\n ),\n );\n\n const first = yield* Stream.mergeAll(completions, { concurrency: \"unbounded\" }).pipe(\n Stream.runHead,\n Effect.mapError((e) => {\n const msg = e.cause instanceof Error ? e.cause.message : String(e.cause);\n return new UserError({ message: `stream failed while waiting: ${msg}` });\n }),\n );\n\n if (Option.isNone(first)) {\n yield* out.warn(\"every instance ended (deleted or stream closed) before a completion\");\n return yield* Effect.fail(new Aborted());\n }\n\n const ev = first.value;\n yield* out.print(completionValue(ev.kind === \"taskCompleted\" ? ev.uploads : []));\n });\n\n/** stdout value for a completion's uploads (`--wait`): a single\n * text/choice/action/multi-choice answer prints as the bare value; anything\n * else (multiple inputs, binary uploads) prints as the uploads JSON. */\nexport const completionValue = (uploads: Upload[]): string => {\n const single = uploads.length === 1 ? uploads[0] : undefined;\n // A multi-choice answer is a list of values; emit them comma-joined as a\n // plain string (mirrors the single-choice `value` line), defined values only.\n const value =\n single && (single.kind === \"text\" || single.kind === \"choice\") ? single.value\n : single && single.kind === \"action\" ? single.key\n : single && single.kind === \"multiChoice\" ? (single.values ?? []).filter((v): v is string => typeof v === \"string\").join(\", \")\n : undefined;\n return typeof value === \"string\" ? value : JSON.stringify(uploads);\n};\n\n/** Send a task to org recipients (a member, a broadcast, or an org topic) over\n * the CLI bearer session via the SDK's OrgClient — the same send surface an\n * Api-Key client uses. Fields (tag/title/content/links + each input's\n * description/default/options) and file bytes are encrypted under the current\n * org master_key when the vault is unlocked. With --wait, the returned handles\n * stream off the org event hub until the first completion. */\nconst sendOrgTask = (params: {\n member?: string;\n broadcast: boolean;\n orgTopic?: string;\n tag: string;\n title?: string;\n content?: string;\n inputs: Input[];\n links: string[];\n files: string[];\n autoCommit: boolean;\n reply?: \"one-shot\" | \"sticky\" | \"one-time-per-user\";\n markdown: boolean;\n noEncrypt: boolean;\n wait: boolean;\n shared: boolean;\n expiresAt?: string;\n format: \"text\" | \"json\";\n}) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n const api = yield* Api;\n const access = yield* VaultAccess;\n\n // Backend requires content OR at least one input.\n if (params.content === undefined && params.inputs.length === 0) {\n return yield* Effect.fail(new UserError({ message: \"an org task needs --content or at least one input\" }));\n }\n\n const vault = yield* access.forSendOrPlaintext(params.noEncrypt);\n const auth = yield* api.session;\n\n const target: OrgSendTarget =\n params.orgTopic !== undefined ? { topic: params.orgTopic }\n : params.member !== undefined ? { member: params.member }\n : { broadcast: true };\n const files = yield* buildFiles(params.files);\n // Left inferred (no `shared` key), so the group send resolves to the\n // TaskGroup overload; the shared branch passes a literal `shared: true`.\n const opts = {\n ...(params.tag ? { tag: params.tag } : {}),\n ...(params.title !== undefined ? { title: params.title } : {}),\n ...(params.content !== undefined ? { content: params.content } : {}),\n inputs: params.inputs,\n links: params.links,\n ...(files.length > 0 ? { files } : {}),\n autoCommit: params.autoCommit,\n ...(params.reply !== undefined ? { reply: params.reply } : {}),\n ...(params.markdown ? { contentFormat: \"markdown\" as const } : {}),\n ...(params.expiresAt !== undefined ? { expiresAt: params.expiresAt } : {}),\n };\n // Full key set (current + history), so --wait can decrypt completion\n // events sealed under an older master_key; sends use the current one.\n const orgMasterKeys = vault\n ? [vault.masterKeyCurrent, ...vault.masterKeyHistory].map((k) => ({ version: k.version, key: k.key }))\n : undefined;\n const enc = vault ? ` (encrypted under master_key v${vault.masterKeyCurrent.version})` : \" (plaintext)\";\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireOrgClient({\n baseUrl: auth.baseUrl,\n bearerToken: bearerToken(auth),\n ...(orgMasterKeys !== undefined ? { orgMasterKeys } : {}),\n });\n\n if (params.shared) {\n // Single shared task: one id/token, all recipients share state.\n const response = yield* sdkCall(\"task send\", () => client.sendTask({ ...target, ...opts, shared: true }));\n yield* out.info(`Org task sent${enc}.`);\n yield* out.info(`Id: ${response.taskId}`);\n yield* out.info(`Append: ${response.appendToken}`);\n if (!params.wait) {\n if (params.format === \"json\") yield* out.print(formatSent(undefined, response.createdAt, [{ id: response.taskId, kind: \"task\", recipient: null }]));\n else yield* out.print(response.taskId);\n return;\n }\n yield* out.info(`waiting for completion of task ${response.taskId}`);\n return yield* waitForFirstCompletion([response]);\n }\n\n // Independent mode (default): one task instance per targeted member.\n const group = yield* sdkCall(\"task send\", () => client.sendTask({ ...target, ...opts }));\n yield* out.info(`Org task group sent${enc}.`);\n yield* out.info(`Group: ${group.groupId} (${group.instances.length} recipient${group.instances.length === 1 ? \"\" : \"s\"})`);\n yield* out.info(`Append: ${group.appendToken}`);\n yield* Effect.forEach(group.instances, (inst) => {\n const who = inst.recipient ? `${inst.recipient.publicId}${inst.recipient.name ? ` (${inst.recipient.name})` : \"\"}` : \"unknown\";\n return out.info(`Instance: ${inst.taskId} -> ${who} append token: ${inst.appendToken}`);\n });\n if (group.instances.length === 0) yield* out.warn(\"the target has no recipients — the group is empty\");\n\n if (!params.wait) {\n if (params.format === \"json\") {\n yield* out.print(\n formatSent(\n group.groupId,\n group.createdAt,\n group.instances.map((i) => ({\n id: i.taskId,\n kind: \"task\" as const,\n recipient: i.recipient ? { publicId: i.recipient.publicId, name: i.recipient.name ?? null } : null,\n })),\n ),\n );\n } else {\n yield* out.print(group.groupId);\n }\n return;\n }\n\n if (group.instances.length === 0) {\n // --wait promises a completion value on stdout; with zero instances\n // none can ever arrive, so this is a failure, not a quiet success.\n yield* out.warn(\"--wait requested but the group has no instances; nothing will complete\");\n return yield* Effect.fail(new Aborted());\n }\n yield* out.info(`waiting for the first completion across ${group.instances.length} instance(s) of ${group.groupId}`);\n yield* waitForFirstCompletion(group.instances);\n }),\n );\n });\n\n","import { Command, Options } from \"@effect/cli\";\nimport { Effect, Option, Stream } from \"effect\";\nimport {\n type SendSubtaskOptions,\n type CreateSubtaskResponse,\n type Client,\n type OrgClient,\n isSubtaskGroupResponse,\n} from \"@simplepush/sdk\";\n\nimport { buildFiles } from \"../files.js\";\nimport { buildInputs } from \"../input-spec.js\";\nimport {\n apiTokenOption,\n baseUrlOption,\n passwordOption,\n quietOption,\n topicOption,\n willEncrypt,\n} from \"../global-options.js\";\nimport { Aborted, UserError } from \"../errors.js\";\nimport { CliOutput } from \"../services/output.js\";\nimport { Api } from \"../services/api.js\";\nimport { VaultAccess } from \"../services/vault-access.js\";\nimport { acquireClient, acquireOrgClient, sdkCall, sdkStream } from \"../services/sdk.js\";\nimport { bearerToken } from \"../services/stores.js\";\nimport { completionValue } from \"./task.js\";\nimport { formatSent } from \"../collect-output.js\";\n\n// A subtask attaches to an existing task's chain via its signed `appendToken`\n// (printed by `sp task`) — not a task id. It carries no targeting: it inherits\n// the parent's recipients and must match the parent's encryption.\nconst appendTokenOption = Options.text(\"append-token\").pipe(\n Options.withDescription(\"The parent task's append token (the `appendToken` printed by `sp task`).\"),\n);\n\nconst titleOption = Options.text(\"title\").pipe(\n Options.withDescription(\"Subtask title.\"),\n Options.optional,\n);\n\nconst contentOption = Options.text(\"content\").pipe(\n Options.withDescription(\"Subtask description / body content.\"),\n Options.optional,\n);\n\nconst linkOption = Options.text(\"link\").pipe(\n Options.withAlias(\"l\"),\n Options.withDescription(\"Attach a remote URL (a link attachment). Repeatable. For local files use --file.\"),\n Options.repeated,\n);\n\nconst fileOption = Options.text(\"file\").pipe(\n Options.withAlias(\"f\"),\n Options.withDescription(\"Attach a local file, uploaded as a file attachment (encrypted under the parent chain's key — topic password or org master key — when the chain is encrypted). Repeatable.\"),\n Options.repeated,\n);\n\nconst textInput = Options.text(\"text-input\").pipe(\n Options.withDescription(\n \"Add a text input. Format: `description[;key=value...]`. Settings: `required=true|false` (default true), `defaultValue=...`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst actionInput = Options.text(\"action-input\").pipe(\n Options.withAlias(\"a\"),\n Options.withDescription(\n \"Add an actions input (buttons the recipient taps, e.g. Accept/Deny). Format: `[description;]key=Label[:style],...[;required=true|false]`, actions comma-separated; style is default|primary|destructive. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst choiceInput = Options.text(\"choice-input\").pipe(\n Options.withAlias(\"c\"),\n Options.withDescription(\n \"Add a choice input. Format: `[description;]options[;key=value...]`, options comma-separated. Settings: `required=true|false`, `multi=true|false` (allow picking more than one option, default false), `minSelections=<int>`/`maxSelections=<int>` (only with multi). Use `\\\\;` for a literal semicolon. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst sliderInput = Options.text(\"slider-input\").pipe(\n Options.withAlias(\"s\"),\n Options.withDescription(\n \"Add a slider input (the recipient picks a number on a scale). Format: `[description;]min=0;max=14;step=0.1;unit=pH;default=7`. `min`/`max` required; `step`/`unit`/`default` optional. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst photoInput = Options.text(\"photo-input\").pipe(\n Options.withDescription(\n \"Add a photo input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst voiceRecordingInput = Options.text(\"voice-recording-input\").pipe(\n Options.withDescription(\n \"Add a voice recording input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst fileInput = Options.text(\"file-input\").pipe(\n Options.withDescription(\n \"Add a file upload input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst locationInput = Options.text(\"location-input\").pipe(\n Options.withDescription(\n \"Add a location input (the recipient shares their device GPS position from the app). Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.\",\n ),\n Options.repeated,\n);\n\nconst submitOption = Options.boolean(\"submit\").pipe(\n Options.withDescription(\"Require the recipient to explicitly submit the subtask. Without this, it auto-completes once the required inputs are filled.\"),\n);\n\nconst waitOption = Options.boolean(\"wait\").pipe(\n Options.withDescription(\"Block until the subtask is completed; print the result to stdout. Requires at least one input on the append. On a group append, the first member's completion wins.\"),\n);\n\n// stdout shape for an append (not `--wait`): `text` prints the bare sub_ id(s)\n// (default), `json` prints a `sent` line carrying the PARENT chain instances —\n// piped to `sp collect`, which streams the chain and stamps the sub_ id on\n// subtask-scoped items. `createdAt` is the append time, so collect backfills\n// from the append, not the parent send.\nconst formatOption = Options.choice(\"format\", [\"text\", \"json\"] as const).pipe(\n Options.withDescription(\"stdout format for an append: `text` (the bare sub_ id(s), default) or `json` (a `sent` line piped to `sp collect`).\"),\n Options.withDefault(\"text\"),\n);\n\n// `--reply <mode>` opts recipients into the composer below the rendered\n// subtask. Wire values match the backend's ReplyMode strings. Absent =\n// no composer (default).\nconst replyOption = Options.choice(\"reply\", [\"one-shot\", \"sticky\", \"one-time-per-user\"] as const).pipe(\n Options.withDescription(\n \"Show a reply composer on the recipient's subtask: 'one-shot' (first reply wins, closes the slot), 'sticky' (open indefinitely), 'one-time-per-user' (one reply per user).\",\n ),\n Options.optional,\n);\n\nconst markdownOption = Options.boolean(\"markdown\").pipe(\n Options.withDescription(\"Render the subtask body as Markdown on the recipient's device (sets contentFormat=markdown).\"),\n);\n\nconst noEncryptOption = Options.boolean(\"no-encrypt\").pipe(\n Options.withDescription(\"For org appends: send fields in plaintext even when the org vault is unlocked.\"),\n);\n\nconst instanceOption = Options.text(\"instance\").pipe(\n Options.withDescription(\n \"With a group append token (grptsk_ group): append only to these member task instances (tsk_ ids printed by `sp task`). Repeatable; without it the subtask goes to every member.\",\n ),\n Options.repeated,\n);\n\nexport const subtaskCommand = Command.make(\n \"subtask\",\n {\n \"append-token\": appendTokenOption,\n title: titleOption,\n content: contentOption,\n \"text-input\": textInput,\n \"choice-input\": choiceInput,\n \"action-input\": actionInput,\n \"slider-input\": sliderInput,\n \"photo-input\": photoInput,\n \"voice-recording-input\": voiceRecordingInput,\n \"file-input\": fileInput,\n \"location-input\": locationInput,\n link: linkOption,\n file: fileOption,\n submit: submitOption,\n wait: waitOption,\n format: formatOption,\n reply: replyOption,\n markdown: markdownOption,\n \"no-encrypt\": noEncryptOption,\n instance: instanceOption,\n topic: topicOption,\n \"api-token\": apiTokenOption,\n password: passwordOption,\n \"base-url\": baseUrlOption,\n quiet: quietOption,\n },\n (args) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n yield* out.setQuiet(args.quiet);\n\n const appendToken = args[\"append-token\"];\n const title = Option.getOrUndefined(args.title);\n const content = Option.getOrUndefined(args.content);\n const topic = args.topic[0];\n const inputs = yield* Effect.try({\n try: () => buildInputs(args),\n catch: (e) => new UserError({ message: e instanceof Error ? e.message : String(e) }),\n });\n\n if (content === undefined && inputs.length === 0) {\n return yield* Effect.fail(new UserError({ message: \"a subtask needs --content or at least one input\" }));\n }\n if (inputs.length === 0 && args.wait) {\n yield* out.warn(\"--wait requested but no inputs were defined; the server will never produce a SubtaskCompleted event\");\n }\n\n const opts: SendSubtaskOptions = {\n ...(title !== undefined ? { title } : {}),\n ...(content !== undefined ? { content } : {}),\n ...(inputs.length > 0 ? { inputs } : {}),\n links: [...args.link],\n autoCommit: !args.submit,\n ...(Option.isSome(args.reply) ? { reply: args.reply.value } : {}),\n ...(args.markdown ? { contentFormat: \"markdown\" as const } : {}),\n };\n const instances = args.instance.length > 0 ? [...args.instance] : undefined;\n\n // Route on credentials, same precedence as `collect`/`daemon`: an API\n // token (--api-token/$SP_API_TOKEN) selects the personal path, otherwise\n // the saved CLI session selects the org path. The append token carries\n // the parent context either way; -t/--topic only picks the encryption\n // key (topic password vs the account default for a topicless self-send\n // parent) and must match the parent's.\n const apiToken = Option.getOrUndefined(args[\"api-token\"]);\n if (apiToken !== undefined) {\n // Personal append: POST /v1/subtasks/json with the API-Token. Read\n // each --file off disk into a FileAttachment; the SDK encrypts the\n // bytes (when the chain has a key) and drives the upload lifecycle.\n const files = yield* buildFiles(args.file);\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireClient({ baseUrl: args[\"base-url\"], apiToken, passwords: args.password });\n if (willEncrypt(args.password, topic)) {\n yield* out.info(\"encrypting outgoing subtask (Argon2id, this takes a moment)\");\n }\n const resp = yield* sdkCall(\"subtask append\", () =>\n client.appendSubtask({\n appendToken,\n ...(topic !== undefined ? { topic } : {}),\n ...(instances !== undefined ? { instances } : {}),\n ...opts,\n ...(files.length > 0 ? { files } : {}),\n }),\n );\n yield* printSubtaskResponse(resp, { format: args.format, wait: args.wait });\n if (args.wait) yield* waitForSubtaskCompletion(client, resp);\n }),\n );\n return;\n }\n\n // -t names a personal topic key; without an API token it cannot mean an\n // org append — refuse rather than silently taking the org path.\n if (topic !== undefined) {\n return yield* Effect.fail(\n new UserError({ message: \"a personal append (-t/--topic) needs an API token (--api-token or $SP_API_TOKEN)\" }),\n );\n }\n\n // Org append: CLI bearer session via the SDK's OrgClient. The parent\n // must be a task in your org.\n yield* sendOrgSubtask({ appendToken, opts, instances, files: [...args.file], noEncrypt: args[\"no-encrypt\"], wait: args.wait, format: args.format });\n }),\n);\n\n/** Shared output contract for both append paths. `text`: the bare sub_ id(s)\n * on stdout (group: one per member, with the taskId -> subtaskId mapping on\n * the info channel). `json`: ONE `sent` line carrying the PARENT chain\n * instances (createdAt = the append time), the machine handle `sp collect`\n * consumes — collect streams the chain and stamps sub_ ids on subtask-scoped\n * items. With `--wait` the stdout contract is the completion value instead,\n * so the json line is suppressed (text ids still print, matching a bare\n * append). */\nconst printSubtaskResponse = (resp: CreateSubtaskResponse, opts: { format: \"text\" | \"json\"; wait: boolean }, suffix = \"\") =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n if (isSubtaskGroupResponse(resp)) {\n yield* out.info(`subtask appended to group ${resp.groupId} (${resp.subtasks.length} member${resp.subtasks.length === 1 ? \"\" : \"s\"})${suffix}`);\n yield* Effect.forEach(resp.subtasks, (s) =>\n out.info(`instance: ${s.taskId} -> subtask ${s.subtaskId}`).pipe(\n Effect.zipRight(opts.format === \"text\" ? out.print(s.subtaskId) : Effect.void),\n ),\n );\n if (opts.format === \"json\" && !opts.wait) {\n yield* out.print(\n formatSent(\n resp.groupId,\n resp.createdAt,\n resp.subtasks.map((s) => ({ id: s.taskId, kind: \"task\" as const, recipient: null, subtaskId: s.subtaskId })),\n ),\n );\n }\n } else {\n yield* out.info(`subtask appended: ${resp.subtaskId}${suffix}`);\n if (opts.format === \"text\") yield* out.print(resp.subtaskId);\n else if (!opts.wait) {\n yield* out.print(formatSent(undefined, resp.createdAt, [{ id: resp.taskId, kind: \"task\", recipient: null, subtaskId: resp.subtaskId }]));\n }\n }\n });\n\n/** Append a subtask to an org task over the CLI bearer session via the SDK's\n * OrgClient, encrypting each field (and any file bytes) under the current org\n * master_key when the vault is unlocked. Mirrors `sendOrgTask` in task.ts; the\n * subtask inherits the parent's recipients. */\nconst sendOrgSubtask = (params: {\n appendToken: string;\n opts: SendSubtaskOptions;\n instances: string[] | undefined;\n files: string[];\n noEncrypt: boolean;\n wait: boolean;\n format: \"text\" | \"json\";\n}) =>\n Effect.gen(function* () {\n const api = yield* Api;\n const access = yield* VaultAccess;\n\n // Auto-encrypt when the org vault is unlocked (prompting inline on a fresh\n // machine instead of silently going plaintext).\n const vault = yield* access.forSendOrPlaintext(params.noEncrypt);\n const auth = yield* api.session;\n\n const orgMasterKeys = vault\n ? [vault.masterKeyCurrent, ...vault.masterKeyHistory].map((k) => ({ version: k.version, key: k.key }))\n : undefined;\n const files = yield* buildFiles(params.files);\n\n yield* Effect.scoped(\n Effect.gen(function* () {\n const client = yield* acquireOrgClient({\n baseUrl: auth.baseUrl,\n bearerToken: bearerToken(auth),\n ...(orgMasterKeys !== undefined ? { orgMasterKeys } : {}),\n });\n const payload = yield* sdkCall(\"subtask append\", () =>\n client.appendSubtask({\n appendToken: params.appendToken,\n ...(params.instances !== undefined ? { instances: params.instances } : {}),\n ...params.opts,\n ...(files.length > 0 ? { files } : {}),\n }),\n );\n yield* printSubtaskResponse(\n payload,\n { format: params.format, wait: params.wait },\n vault ? ` (encrypted under master_key v${vault.masterKeyCurrent.version})` : \" (plaintext)\",\n );\n if (params.wait) yield* waitForSubtaskCompletion(client, payload);\n }),\n );\n });\n\n/** Block until the appended subtask completes and print the answer, mirroring\n * `sp task --wait`. Rehydrates observe handles from the append response ids\n * (single: the one subtask; group: every minted sibling, first completion\n * wins) and streams their `inputs()` off the shared event hub. A sub-stream\n * that ends without completing (canceled, declined, chain deleted) ends\n * benignly; no completion anywhere is a failure (exit 1, empty stdout). */\nconst waitForSubtaskCompletion = (client: Client | OrgClient, resp: CreateSubtaskResponse) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n\n const watches = isSubtaskGroupResponse(resp)\n ? resp.subtasks.map((m) => client.watchSubtask({ subtaskId: m.subtaskId, taskId: m.taskId, createdAt: resp.createdAt }))\n : [client.watchSubtask({ subtaskId: resp.subtaskId, taskId: resp.taskId, createdAt: resp.createdAt })];\n\n if (watches.length === 0) {\n // A group append to an empty (or fully filtered) roster mints nothing;\n // --wait promises a completion value on stdout, so fail rather than hang.\n yield* out.warn(\"--wait requested but the append minted no subtasks; nothing will complete\");\n return yield* Effect.fail(new Aborted());\n }\n yield* out.info(\n watches.length === 1\n ? `waiting for completion of subtask ${watches[0]!.subtaskId}`\n : `waiting for the first completion across ${watches.length} subtask instance(s)`,\n );\n\n const completions = watches.map((w) =>\n sdkStream(\"subtask wait stream\", (signal) => w.inputs({ replay: true, signal })).pipe(\n Stream.filter((ev) => ev.kind === \"subtaskCompleted\"),\n Stream.take(1),\n ),\n );\n const first = yield* Stream.mergeAll(completions, { concurrency: \"unbounded\" }).pipe(\n Stream.runHead,\n Effect.mapError((e) => {\n const msg = e.cause instanceof Error ? e.cause.message : String(e.cause);\n return new UserError({ message: `stream failed while waiting: ${msg}` });\n }),\n );\n\n if (Option.isNone(first)) {\n yield* out.warn(\"every subtask ended (canceled, declined, or chain deleted) before a completion\");\n return yield* Effect.fail(new Aborted());\n }\n const ev = first.value;\n yield* out.print(completionValue(ev.kind === \"subtaskCompleted\" ? ev.uploads : []));\n });\n","// Shebang is added by tsdown via its `banner` option; do not duplicate it here, or\n// rolldown emits DUPLICATE_SHEBANG and produces an empty bundle.\n//\n// Composition root: the full Layer graph (platform services + the CLI's own\n// services) is provided ONCE here, and the top-level catchAllCause is the only\n// place errors become stderr text (see errors.ts). runMain's teardown exits\n// the process, which is also what keeps undici's keep-alive sockets from\n// holding the event loop open after `auth login`.\n\nimport { Command } from \"@effect/cli\";\nimport { FetchHttpClient } from \"@effect/platform\";\nimport { NodeContext, NodeRuntime } from \"@effect/platform-node\";\nimport { Cause, Effect, Layer, Option } from \"effect\";\n\nimport { renderError } from \"./errors.js\";\nimport { CliOutput } from \"./services/output.js\";\nimport { Sodium } from \"./crypto/sodium.js\";\nimport { AuthStore, InviteStore, VaultStore } from \"./services/stores.js\";\nimport { Api } from \"./services/api.js\";\nimport { VaultAccess } from \"./services/vault-access.js\";\n\nimport { authCommand } from \"./commands/auth.js\";\nimport { cancelCommand } from \"./commands/cancel.js\";\nimport { collectCommand } from \"./commands/collect.js\";\nimport { daemonCommand } from \"./commands/daemon.js\";\nimport { downloadCommand } from \"./commands/download.js\";\nimport { eventsCommand } from \"./commands/events.js\";\nimport { notifyCommand } from \"./commands/notify.js\";\nimport { orgCommand } from \"./commands/org.js\";\nimport { integrationCommand } from \"./commands/integration.js\";\nimport { taskCommand } from \"./commands/task.js\";\nimport { subtaskCommand } from \"./commands/subtask.js\";\n\n// Streams can also surface a broken pipe as an async 'error' event (bypassing\n// CliOutput's sync try/catch — e.g. @effect/cli's own help output). Same\n// contract as CliOutput: stdout's reader gone → done, exit clean; stderr is\n// best-effort. Anything else stays fatal.\nconst onPipeError = (exit: boolean) => (e: NodeJS.ErrnoException) => {\n if (e.code !== \"EPIPE\") throw e;\n if (exit) process.exit(0);\n};\nprocess.stdout.on(\"error\", onPipeError(true));\nprocess.stderr.on(\"error\", onPipeError(false));\n\nconst root = Command.make(\"simplepush\").pipe(\n Command.withSubcommands([authCommand, orgCommand, integrationCommand, eventsCommand, collectCommand, daemonCommand, downloadCommand, notifyCommand, taskCommand, subtaskCommand, cancelCommand]),\n);\n\nconst cli = Command.run(root, {\n name: \"Simplepush CLI\",\n version: \"0.2.0\",\n});\n\n// Service layers. Each service's `dependencies` covers its siblings; the\n// platform capabilities (FileSystem, Path, Terminal, HttpClient) come from\n// NodeContext + FetchHttpClient underneath. Layer memoization guarantees one\n// instance of each service per run.\nconst MainLive = Layer.mergeAll(\n CliOutput.Default,\n Sodium.Default,\n AuthStore.Default,\n VaultStore.Default,\n InviteStore.Default,\n Api.Default,\n VaultAccess.Default,\n).pipe(\n Layer.provideMerge(FetchHttpClient.layer),\n Layer.provideMerge(NodeContext.layer),\n);\n\n/** Render any failure through the typed-error table, then re-fail so the\n * runtime exits non-zero. Defects (bugs) keep their full pretty cause. */\nconst reportErrors = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n Effect.catchAllCause((cause) =>\n Effect.gen(function* () {\n const out = yield* CliOutput;\n if (!Cause.isInterruptedOnly(cause)) {\n const failure = Cause.failureOption(cause);\n if (Option.isSome(failure)) {\n const message = renderError(failure.value);\n if (message !== undefined) yield* out.error(message);\n } else {\n yield* out.error(Cause.pretty(cause));\n }\n }\n return yield* Effect.failCause(cause);\n }),\n ),\n );\n\ncli(process.argv).pipe(\n reportErrors,\n Effect.provide(MainLive),\n NodeRuntime.runMain({ disableErrorReporting: true }),\n);\n"],"mappings":"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"}