chore: rename Myme to Marfa wholesale#68
Open
aicayzer wants to merge 27 commits into
Open
Conversation
Pulls in the Myme TypeScript SDK at 5.6.0. First step of the optional Myme integration — no runtime code uses it yet, but locking the version in for the rest of the work.
Adds the two custom-type schemas that the Myme integration will write against (recording inherits from core.note; session is standalone) and a one-off registration script that posts them to the configured tenant. The script reads ~/.myme/admin.json for direct-admin access; the runtime integration uses the OAuth device flow instead. Types are registered against staging.myme.so as part of bringing this milestone up — re-running the script is a no-op upsert.
Adds the Settings → Integrations → Myme card, the IPC surface for the integration, and a stub main-side module that satisfies the contract end-to-end. The card renders four states off the MymeStatus discriminated union (disconnected / connecting / connected / syncing) plus a renderer-composed 'disabled' state when demo mode is on or no recordings path is configured. Endpoint defaults to https://staging.myme.so and persists in config.json via an additive migration. Real OAuth + sync land in milestones 3+; the stubs let the renderer exercise every state today. Smoke-tested in dev: the card renders correctly in disconnected, connecting, and disabled states against the real 11.8k-recording dataset.
Connects the Settings → Integrations → Myme card to the Myme SDK end-to-end. The 'Connect' affordance now opens a paste-API-key pane; the supplied key is verified via items.stats(), encrypted with Electron's safeStorage (Keychain-backed on macOS) and persisted to <userData>/myme-credential.enc. Boot-time credential probe restores the connected state across launches. Bypasses the OAuth device flow the integration spec mandates — staging's OAuth path can't be bootstrapped via dynamic client registration today, and the well-known doesn't advertise the device_code grant. See the running log for the full chain of findings; API key is the worktree-experiment substitute that unblocks milestones 4+. Also fixes the @mymehq/sdk ESM-only packaging issue by adding the SDK to electron-vite's externalizeDeps exclude list — without this, the bundled main process tries to require() the ESM SDK at runtime and crashes with ERR_PACKAGE_PATH_NOT_EXPORTED. Smoke-tested against staging.myme.so: paste-and-connect with a valid key flips to connected; bad key surfaces 'Invalid API key' inline; disconnect clears the credential file and reverts to disconnected.
Builds the per-recording projection layer (Recording → Myme item payload with stable content hashes), the on-disk sync-state file (per-source_id hash + server-assigned itemId + last-pushed time; atomic write-temp + rename), and the engine that drives the diff: new → upsert, hash-mismatch → upsert, vanished → transition trashed, match → no-op. Substitutes parallel singular items.upsert for the spec's items.bulk path — bulk is admin-only on the server side, which makes it unreachable from any out-of-tree member-role integration. Caps concurrency at 10 to stay under staging's 2000 req/min budget while keeping the channel saturated. Smoke-tested against staging: Sync now flips the card to 'Syncing recordings 0 / 11,765 → 20 / 11,765 …', items land on the server, status returns to 'connected' after the run. The initial 11.7k-recording push is bottlenecked by per-request latency — subsequent reindexes only diff the delta, so the steady-state cost is small. Tests: 14 new (projection field selection + hash stability, state load/save/clear + atomic write + defensive parse).
Adds an onReindexed listener registry to cache.ts and threads it through the rescan exits. The Myme integration registers a single listener at app.ready that fires syncNow() fire-and-forget whenever the cache rebuilds — fs.watch debounce hits, manual Reindex from Settings, demo-mode toggles. The engine's syncInFlight guard coalesces overlapping runs, so back-to-back watcher events don't spawn parallel syncs. The diff semantics for mutation handling and disk-delete propagation already landed with the engine in the previous commit (hash-mismatch → upsert, vanished source_id → transition trashed). This commit lights up the trigger so they actually run without a manual Sync now click.
Adds gap-grouping (pure, in sessions.ts) and the session-projection + engine pass that mints superwhisper.session items with inline core.parent-of edges to the constituent recordings. The natural key is (first_recording_id, gap_threshold_minutes), so a threshold change yields fresh source_ids — the engine's diff naturally trash-and-re-mints the prior session set without in-place updates. Trash-and-re-mint over in-place is the resolution to the open question in the integration spec. Edge writes use the inline edges field on items.upsert, sourced from the recording itemIds resolved in the same sync pass. If a recording's upsert failed earlier in the run, the session keeps the partial edge set rather than failing whole — the next sync fills in the missing edge. Tests: 9 new gap-grouping cases (threshold boundaries, dominant mode + tie-break, out-of-order input, source_id stability).
Adds scripts/myme-smoke.mts — a deterministic 5-recording smoke against staging.myme.so that exercises every diff path from the integration spec: 1. first-run push (5 recordings + 1 session + 5 parent-of edges) 2. mutation handling (edit one recording, verify version bump) 3. disk-delete propagation (drop one, verify trashed state) 4. threshold-change re-mint (bump gap, verify fresh source_ids) Decoupled from the on-disk state file so it can be re-run cleanly: teardown step trashes every prior superwhisper.* item in the tenant before each run. Reads admin creds from ~/.myme/admin.json. Run with 'pnpm dlx tsx scripts/myme-smoke.mts'. Existence + size kept small so it doesn't paper over the real-corpus push performance problem documented in the running log.
Two affordances added to the Settings → Myme card:
- Cancel button in the syncing state. The store calls a new
myme:cancelSync IPC that aborts the active sync's
AbortController. The engine checks signal.aborted between
items.upsert / items.transition calls and persists everything
that landed before the abort, returning with error =
'Cancelled'. In-flight requests can't be cut short via the
public SDK, so an aborted upsert's catch handler now
suppresses the resulting transport error if the signal fired
while it was in flight — keeps 'Cancelled' as the canonical
outcome rather than letting a timeout / network error
overwrite it.
- 'Push N most recent (testing)' knob in the connected state.
Persisted as Config.myme.syncLimit (additive migration,
default 0 = full sync). When the limit is in effect the
engine slices recordings.slice(0, n) (newest-first per
scanner) and skips the soft-delete + session passes entirely
— both are all-or-nothing concepts that would be incoherent
against a partial recording view.
Smoke-tested locally: setting the knob to 5 and clicking Sync now
flips to 'Preparing… 0 / 5' with a visible Cancel button. Engine
respects the limit (5 attempted upserts, not 11.7k).
… setting (#57) The 'push N most recent (testing)' knob has been the de-facto sync-cap control since the engine landed, but it was framed as a debug toggle — default 0 (uncapped), dashed-border styling, "testing" copy. This promotes it to a real setting in the Integrations tab. Changes: - `src/main/config.ts` — default `syncLimit` flips from 0 → 100. Protects against first-sync floods on large corpuses (the wider motivation for the wave); explicit `0` still means no cap for users who want to test the full sync surface. - `src/renderer/.../MymeCard.tsx` — reframe the row: title "Sync most recent N recordings"; copy "0 = no cap. When capped, session derivation and disk-delete propagation are skipped — turn off the cap to test the full sync surface."; drop `border-dashed`; update the card's module-doc + the `SyncLimitRow` JSDoc. - `src/main/myme/engine.ts` — cap-gate comments in the soft-delete pass + session-derivation pass drop the "testing-knob" framing and point at the UI surface for the trade-off rationale. Engine behaviour unchanged (all-or-nothing under a cap; partial-view safety). Trade-off remains intentional and now explicit: capping disables session derivation + disk-delete propagation. Users who want both can flip the cap off; users who don't want to flood Myme on first sync get a sensible default. Refs T-165.
…#58) Default Connect now enters the OAuth device flow (SDK 5.7.1's `startDeviceFlow` from `@mymehq/sdk/auth`); the renderer shows the user code + a "Verify in browser" button (deep-link variant when provided), with a copy-to-clipboard and a "Use API key instead" link to the existing paste path. - Tokens persist via `safeStorage`-encrypted JSON blob — discriminated union of `{ kind: 'api-key', key }` and `{ kind: 'oauth', clientId, tokens }`. Legacy bare-string credentials up-convert transparently so existing API-key users don't get logged out. - `MymeClient` builder accepts the new OAuth credential by wrapping it in a minimal `TokenProvider` that proactively refreshes when < 60s remain, single-flight, persists rotated bundles back to disk. - DCR (`POST /auth/oauth2/register`) runs once per `connect()` so each device ends up with its own client id alongside the tokens. - IPC surface adds `myme:useApiKey` (switch into the api-key pane) and `myme:cancelConnect` (abort an in-flight device-flow poll). `connect` is now async. - State-machine tests cover device-flow happy path, user-denied, code-expired, DCR network failure, and cancel-mid-flow. Renderer ceremony stays out of unit-test scope (covered by T-166 e2e walk later). Tokens round-trip tests pin the on-disk format + legacy up-conversion.
…59) The DCR scopes in `DEFAULT_SCOPES` were placeholders (`['*:read', '*:write']`) — bare wildcards that aren't in the Myme server's scope allowlist. Live test in the test app produced `Client registration failed: invalid_scope (400)` on first connect. The server's allowlist is built from `TYPE_REGISTRY.keys() + EDGE_TYPE_REGISTRY.keys()` plus OIDC standards. Replace the placeholder with the genuine surface this app uses: - `superwhisper.recording:{read,write}` — data plane - `superwhisper.session:{read,write}` — derived sessions - `metadata.types:write` — registers the custom types on first sync - `openid`, `profile`, `email`, `offline_access` — OIDC standards + refresh-token issuance Narrower than the API-key path's effective blast radius (admin keys have everything) but accurate to what the engine actually touches — matches the T-164 ticket's own note ("Tighten when the engine settles on what it actually needs"; we now know what it needs). Caught during T-166 e2e validation. Unit tests didn't surface this because they mock the SDK transport — only the live server's scope validator rejects bare wildcards. Refs T-164.
Audit pass on the engine: session derivation mints a `parent-of` edge from session → recording (`engine.ts:464`). The OAuth grant must carry matching edge scopes for the server's `requireEdgePermission` gate; otherwise uncapped syncs would 403 the moment sessions try to write. Add the two scopes: - `edge.parent-of:read` - `edge.parent-of:write` Capped-mode runs (cap > 0) skip the sessions pass entirely, so this gap didn't surface in the initial T-166 device-flow validation (which was capped). Surfaces immediately under uncapped sync. Refs T-164.
… to 'parent-of' (#61) The engine wrote session→recording edges with the wrong type literal: edges: { 'core.parent-of': recordingItemIds } The server's registered edge type id is `parent-of` — no `core.` prefix — per `packages/types/core/edges/parent-of.json`. The wrong literal would have failed the server's edge-type validator at write time with an `edge type not registered` error. Latent because no prior testing exercised it: - API-key flow: all prior staging testing was capped, and capped runs skip the sessions pass entirely (engine all-or-nothing semantics). - OAuth flow: just added in T-164; uncapped T-166 validation would have hit this immediately. Caught during the T-164 scope audit (grep for edge writes in the engine, looking for the matching `edge.<type>:write` scopes). Fix: one-line edit at engine.ts:464 + three comment cleanups across engine.ts and sessions.ts to keep the documentation honest. Refs T-172.
Replace the hardcoded `superwhisper.recording` / `superwhisper.session` type IDs in the projection layer with a `MymeMapping` config block that binds each source kind (recording, session) to a target type and a field map. Default = bundled, identity field map — existing source_ids stay stable for users mid-migration. Non-bundled bindings get an 8-char fingerprint folded into source_ids so swapping the binding produces fresh ids and old items diff out on the next sync (trash-and-re-mint, mirroring the existing gap-threshold pattern). Bundled uses a sentinel suffix-free shape. Adds: - `mapping.ts` — types, defaults, fingerprint helper, default field-map generator with a name-alias table for auto-pairing target fields. - `registration.ts` — ensures bundled / authored types exist server-side on each sync; idempotent. - IPC: `getMapping`, `setMapping`, `getModeFilter`, `setModeFilter`, `probeConnection`, `listTypes`, `registerType`. - Mode filter wiring through the engine. - `mapping.test.ts` + extended `projection.test.ts`. Default mapping persists transparently for older configs; the engine clears sync state on mapping change so the next pass starts from a clean diff.
Adds a MappingPanel inside the connected MymeCard with two rows (recordings + sessions) showing the current binding and an Edit action. Edit expands an inline editor offering three modes: - Bundled — app-defined default, fixed field map. - Existing — pick a type already in the user's Myme tenant via `types.list()`; auto-pair recording fields to target fields by name alias. - Authored — define a new type from scratch via an inline form; registers the schema on Apply. Apply gates behind a trash-and-re-mint confirmation explaining that old items soft-delete on next sync. Cancel discards the draft. The panel reads the type list lazily on first edit, falls back gracefully when the list fails to load, and validates authored type ids before registration. Store gains mapping CRUD, type-list cache (with loading flag), mode-filter mirroring, probe and register-type passthroughs. Read on demand; main is source of truth.
Splits the connected MymeCard into three composable panels: - ConnectionPanel — endpoint, account identity from `profile.get`, reachability indicator with reason on failure, Test connection + Disconnect actions. - MappingPanel — unchanged from PR 2; slots in unmodified. - SyncControlsPanel — sync-cap (reframed copy), mode-filter chips derived from observed Superwhisper modes, last-sync drift, recent- error row, manual sync trigger. Mode chips compute counts client-side from the indexed dataset; clicking a chip toggles the persisted mode filter (`null` = sync everything, list = sync only those modes). The engine reads the filter from config on each sync. Test connection runs `profile.get()` on mount + on click; result lands in local state and renders a green pill on success or the error inline on failure. No bespoke ping endpoint — the cheapest authed call covers both the reachability check and the identity display.
* feat(sw-app): redesign the connected card layout The original layout buried the type mapping behind an Edit / Close gate, opened with Connection (the least frequently touched part), and gave every subsection the same border treatment — flat and patchwork. This rework treats the card as three clearly-separated sections with a consistent header pattern (uppercase tracked label + subtitle): - Type mapping (top — the headline decision) - Sync (middle — what lands, when) - Connection (bottom — compact footer) Type mapping is now always-visible: each binding card shows the segmented [Bundled | Existing | Authored] control inline, the type id in monospace, and the field map open. Bundled fields render read-only with a 'Fixed by the bundled type.' note; existing and authored modes expose the field-picker UI. Apply only appears when the draft differs from the persisted binding, and still gates the trash-and-re-mint warning before committing. Resetting the binding is one click rather than 'Cancel + re-open Edit.' Connection shrinks to a compact footer: endpoint + identity on the left, a status pill on the right, Disconnect / Test connection in a small button row below. Sync gains an inline Sync-now button next to the Last-synced row so the common action sits next to its status. Renderer-only — no engine, IPC, or main-process changes. * refactor(sw-app): split the Myme tab into three Settings cards Replaces the single MymeCard with three sibling SettingsCards that match the rest of the Settings tabs (Indexing / Filler words shape): Connection · Sync · Type mapping. The integration tab in the segmented strip renames from 'Integrations' to 'Myme'. Changes: - Connection (top, Cloud icon) — endpoint, account identity, status pill, Disconnect + Test connection. Pill moves into the header via the SettingsCard.headerExtra slot. - Sync (middle, RefreshCw icon) — mode filter rendered as toggle rows per observed Superwhisper mode (matches Watch folder pattern), sync cap, Last-synced display with inline Sync now / Cancel buttons. During syncing, the header pill shows progress and the body switches to phase + processed/total. - Type mapping (bottom, Layers icon) — binding panels for recording and session. Field map laid out with CSS subgrid so target / arrow / source columns line up across rows regardless of name length. Authored / Existing modes expose the field-picker inline; bundled renders the canonical layout read-only with a fixed-source label. Buttons are unified on the standard chrome treatment (no 'primary' variant). The disconnect/connect flow lives in its own Connect-to-Myme card during disconnected / connecting states, so the empty-state copy matches the connected hierarchy. Drops the three superseded panel files (MappingPanel, ConnectionPanel, SyncControlsPanel) — fully replaced by the new SettingsCard-wrapped components. * refactor(sw-app): drop sync-cap UI, add test-sync button, polish copy The sync-cap had nasty semantics — skipped soft-delete, skipped session derivation, orphaned items beyond the cap — and it was a testing knob that leaked into the user surface. Drop it. - Remove `syncLimit` from Config.myme, MymeStatus, the setSyncLimit IPC + store action. Engine's `limit` option stays for tests but no production code path sets it. - New `testSync` IPC + store action: runs `syncRun` with limit=5 for a quick sanity-check pass. Surfaced in the Sync card as a secondary 'Test sync' button next to 'Sync now'. Field-map grid: target sits flush left, source cluster (arrow + value + remove) sits flush right. A spacer column between the two halves keeps that split stable regardless of target-name length. Subgrid carries the column widths across rows. Copy pass: tighten the card subtitles, retire 'Local-only by default' for 'Off by default — local-only until you connect', rewrite the trash-and-re-mint warning, replace generic 'No fields mapped' with action-oriented prompts. All buttons unified on the chrome treatment. * fix(sw-app): inline arrow next to source on field rows Drop the cross-row arrow alignment. Each row uses flex justify-between so the target sits on the left and the arrow+source cluster sits on the right, trailing the row. Same spacing for both bundled (read-only) and editable variants. * fix(sw-app): align arrows in a fixed centre column on the field map Target column is max-content (locks to the longest target name); arrow column auto; source column 1fr right-aligned. Result: arrows sit at the same x across every row, source labels hang flush right. * fix(sw-app): drop the arrow from field rows The arrow column added noise without clarifying the relationship. Target flush left, source flush right reads as the mapping just fine on its own.
Local-only patches so this build can be installed in /Applications alongside the v0.2.x release without sharing config, Myme tokens, or sync state, and without the upstream auto-updater clobbering it. - appId: me.cyzr.superwhisper-analytics-myme - productName: SuperWhisper Analytics (Myme) - userData dir redirected to superwhisper-analytics-myme - publish stanza dropped; initAutoUpdater() call removed Lives on local/myme-install branch, never pushed.
Importing ./updater pulls electron-updater into the bundle, and the pnpm-packaged asar is missing transitive deps (e.g. `ms`), which crashes the main process at module load. The myme install build doesn't auto-update anyway, so the renderer's "Check for updates" button now silently no-ops. The shipped main branch has the same packaging issue and will need a proper fix (likely node-linker=hoisted in .npmrc) before any release exercising the auto-updater code path can be cut.
…vice Pairs with the monorepo property-shadow ban in T-203; companion to vault ticket T-204. Bumps superwhisper.recording schema to v2. - schemas.ts: rename field, update description, version 1 → 2 - mapping.ts: rename target key + source-field-ref + alias value - projection.ts: rename case label in readRecordingField - engine.ts: populate first-class item.device from os.hostname() - TypeMappingCard.tsx: mirror rename in renderer-side defaults - projection.test.ts: update expected property keys
…lter Follow-on to commit 114d11e (T-204 property rename). Fixes the end-to-end story so the rename actually lands on existing installs: - config.ts + mapping.ts: forward-migrate persisted mappings that reference the stale `recording.device` source-ref so installs from before the rename project the device value into the new shape instead of silently dropping it. Bundled bindings are rebuilt from the canonical map; authored / existing bindings only get the source-ref rewritten so user-defined target keys are preserved. - registration.ts: re-register when the local schema version is higher than the server's, not only when the type is absent. Without this, a bundle bump like v1 → v2 never propagates to a server that already has the older row, so projection writes input_device but the server validates against the v1 schema and drops it. - mapping.ts: add the `input_device` key to the recording-field alias table so authored types declaring an `input_device` field auto-pair correctly, and rename the field in `authoredRecordingStarter` to match (the previous `device` field would now trip T-203's property_shadows_field ban). - engine.ts: drop recordings with empty `result` before projection. Superwhisper writes meta.json files with empty transcripts for failed captures; projecting them produces a payload missing the required `body` field (inherited from core.note), which 400s on /items and halts the sync via the validation path. Tests added for migrateRecordingMapping (4 cases) and ensureTypesRegistered (7 cases).
- Tabs: General / Analysis / Sync / Developer / About (was four; Sync separated from a single Myme card). - Tokens: accent-green and border-strong added in both light + dark. - Buttons: default border-radius bumped 4→6 (rounded-md → rounded-lg across all sized variants of the primitive). - General: Recordings folder card de-monoed and de-chunked; identity header, plain-text path bar, single muted stats line, icon-only Reindex button in the top-right. Appearance picker rebuilt with larger window mocks that read as the top of a real window, single sidebar in the System tile, monochrome active state. - Analysis: filler dictionary regrouped into six plain-English categories (Hesitations / Conversational fillers / Softeners / Emphasis words / Uncertainty and corrections / Vague references). Categories drive the UI; the flat phrase list is derived. Session gap moved here as a 15/30/60/120-min dropdown — canonical home, unification with the engine still to come. - Sync (shell only — sign-in flow is a follow-up): ConnectionCard rebuilt with six visible states including device-flow code render; Pipeline cards (Recordings / Sessions); MappingRoot inline editor with DestinationPickerSheet + FieldSourcePickerSheet; SyncActionBar. - Developer: single Developer card carrying Demo data + DevTools toggles. Separate App data card with the Reset action (moved out of About). - About: License first, then Source, Version, Updates. No `v` prefix on the version. Auto-checks for updates on mount. - Switches: row alignment switched to items-center across Developer, Indexing, Transcripts, AppData (was items-start). - Engine: pipeline-enabled gates honoured (no upserts and no soft- deletes when off). Session gap read from config at sync time. - Config: additive migration for `recordingPipelineEnabled`, `sessionPipelineEnabled`, `sessionGapThresholdMinutes`. IPC handlers added for each. - Shared: filler-categories.ts as the canonical filler source; text-metrics.ts re-exports a flat DEFAULT_FILLER_PHRASES derived from it. myme-labels.ts holds plain-English source-field labels for the picker sheets.
- AppearancePicker: System tile first; sidebar halved (~17% of the window width); content tone softened so it sits between the sidebar grey and the tile background; monochrome active state (foreground/40 border + ring) replaces the accent-blue; subtle wash on all three tiles for consistency with the path bar. - FillerDictionaryCard: title row given a fixed min-height so the switch stays vertically centred against the title whether the category is collapsed or expanded — no more upward drift on expand. Preview line + chip cloud indented by 22px (chevron width + gap) so they line up with the title text rather than the chevron. Pills rounded-full. "new phrase" placeholder (no ellipsis). Add input switched to onKeyDown (Enter saves, Escape cancels) — the form wrapper was eating submits. Custom rendered as a regular toggleable row when populated. - RecordingsFolderCard: stats line split — "12k recordings · 279 hours of audio" left, "Indexed Xm" right (capital I). Reindex icon button stays top-right. - SessionGapCard: dropdown replaced with a SegmentedTabs picker (15m / 30m / 60m / 120m). self-center passed in so it aligns to the label block, not the row top.
UI
- ConnectionCard final shape — outlined SyncSplitButton (matches
CHROME_BUTTON, no shadow, no black face) used across every connected
sub-state with different labels: Start sync / Sync now / Resume /
Retry. Internal pipe + chevron-dropdown for secondary actions. No
more separate primary + overflow controls.
- StatusPill in the header reflects the connection state only —
"Connected" stays even when the last sync failed; the failure surfaces
as a muted body caption instead of an orange-bordered panel.
- Identity collapses to two rows: Account ("name (email)") + Endpoint.
- Caption uses the new X-of-Y synced counts: "X of Y recordings synced
· Z ago" / "Sync cancelled · X of Y synced" / "Sync failed: <reason>
· X of Y synced".
- Field source picker rebuilt as a two-column grid of compact tiles.
Each tile carries plain label + canonical ref subtitle. No type tag,
preview value, or radio icon — selection is conveyed by border + tint.
Footer flipped: caption left, "Remove this field" right.
- Destination picker rebuilt with the same tile pattern. Default tile
on its own; custom types in a grid below a search input.
- MappingRoot de-monoed end to end. Plain sans-serif type IDs and
field names.
- PipelineCard session-gap stepper replaced with a read-only mirror of
the canonical value on the Analysis tab. Modes section dropped its
accent-blue affordances for a monochrome treatment.
- Wipe-all-data moved off the Sync tab back to the Developer tab where
the other testing affordances sit; gets a native confirm prompt.
Toasts
- Sonner-backed toast system. toastError / toastInfo / toastSuccess
helpers. Failure renders destructive red on a faint red tint;
success renders accent-green on a faint green tint. Padding tighter
than the default. Action buttons (Copy logs) outlined rather than
the heavy black face. App.tsx mounts the Toaster + a transition
watcher that fires toastError on lastError changes only.
- ESLint no-restricted-imports blocks bare `sonner` imports
everywhere except the wrapper modules.
Backend
- `MymeStatus.connected` extended with `syncedRecordings`,
`syncedSessions`, `lastSyncCancelled`. Distinguishes a user-cancelled
sync (Resume) from a genuine failure (Retry).
- `connectedStatus()` helper centralises status construction across
the four transitions (initial boot, device-flow success, API-key
success, post-sync). `buildInitialStatus()` now reads
`lastFullSyncAt` from engine state on boot instead of hardcoding null.
- SDK + OAuth wiring fixes from the parallel sign-in pass — fresh
`SafeStorageTokenStorage`, `token-storage.ts`, and DCR scope
corrections that unblock the device flow.
- SyncActionBar component removed — ConnectionCard owns the sync
controls.
Picks up the T-218 async bulk_action substrate. The SDK's bulkAction()
signature is unchanged — it polls internally and resolves with the
same BulkActionResult — so the Developer-tab Purge button stops
timing out at 30 seconds without any code change here.
src/main/myme/index.ts:512 already calls client.items.bulkAction({
action: 'purge', confirm: 'PURGE', ... }) for both recordings and
sessions. Both calls now poll under the hood; the previous 30s
ceiling that surfaced this whole work item is gone.
T-218.
Wholesale rename across the SuperWhisper Analytics codebase to match the Marfa cutover. Substitution pattern: mymehq -> withmarfa, MYME_ -> MARFA_, Myme -> Marfa, myme -> marfa, @mymehq/* -> @withmarfa/*. Dep pin flipped from @mymehq/sdk@^5.9.0 to @withmarfa/sdk@^1.0.0; lockfile regenerated resolves cleanly from npm. Source dir src/main/myme -> src/main/marfa, state/mymeStore -> state/marfaStore, shared/myme-labels -> shared/marfa-labels, scripts/myme-* -> scripts/marfa-*. Typecheck passes; all 148 tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wholesale Myme to Marfa rename across the SuperWhisper Analytics codebase to match the broader Marfa cutover (T-263). Substitution pattern matches the main Marfa repo group:
mymehq->withmarfaMYME_->MARFA_Myme->Marfamyme->marfa@mymehq/*->@withmarfa/*@^1.0.0Dep pin flipped from
@mymehq/sdk@^5.9.0to@withmarfa/sdk@^1.0.0. Lockfile regenerated;@withmarfa/sdk@1.0.0resolves cleanly from npm. Source paths renamed:src/main/myme/->src/main/marfa/,state/mymeStore.ts->state/marfaStore.ts,shared/myme-labels.ts->shared/marfa-labels.ts,scripts/myme-*->scripts/marfa-*.Test plan
pnpm typecheck— cleanpnpm test— 148 passed (15 files)git grep -nE '(mymehq|MYMEHQ|MYME_|myme_|Myme[A-Z]|\bmyme\b)'returns 0 matches@mymehq/*entries