@echomem/mcp 1.4.20 → 1.4.21

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.
@@ -93,30 +93,17 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
93
93
  '<div class="actions"><button id="logout" class="secondary">Retry sign out</button></div></div>';
94
94
  var lo = document.getElementById("logout"); if (lo) lo.onclick = localLogout;
95
95
  }
96
- async function waitForConnection() {
97
- if (connectionPollStarted) return;
98
- connectionPollStarted = true;
99
- try {
100
- for (;;) {
101
- if (decisionMade) return;
102
- try {
103
- var cfg = await getJson("/config");
104
- authUrl = cfg.authUrl || authUrl;
105
- switchAccountUrl = cfg.switchAccountUrl || switchAccountUrl || authUrl;
106
- if (cfg.connected) { connected = true; closeAuthWindow(); await waitForStats(); return; }
107
- } catch (_) {}
108
- await sleep(900);
109
- }
110
- } finally { connectionPollStarted = false; }
111
- }
112
- function startConnectionPoll() { void waitForConnection(); }
113
96
  async function waitForStats() {
114
97
  if (statsPollStarted) return;
115
98
  statsPollStarted = true;
116
- // Use the same one-reading-line stage as the partial scan state. This avoids a jarring
117
- // old setup shell between device approval and the ready decision.
118
- renderDashboard();
99
+ setCityMode(false);
100
+ setExtractMode(false);
101
+ setReadyMode(true);
102
+ setHead("Checking extraction", "Working");
103
+ app.className = "loading-panel";
104
+ app.innerHTML = '<div style="padding:24px"><h2>Checking for active work</h2><p class="helper">Echo is reconnecting to any extraction already running on this machine.</p></div>';
119
105
  try {
106
+ if (await resumeExistingExtraction()) return;
120
107
  var started = Date.now();
121
108
  var lastError = "";
122
109
  for (;;) {
@@ -301,21 +288,74 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
301
288
  setHead("Extracting", "Starting");
302
289
  app.className = "";
303
290
  app.innerHTML = '<div class="miniLoad"><h2>Starting extraction</h2><p>Echo is sizing the remaining jobs, then imports automatically. Finished work is saved as it goes.</p><div class="miniBar"><i></i></div></div>';
291
+ var started;
304
292
  try {
305
- var started = await postJson("/migrate", { conversationKeys: selectedKeys });
306
- extractMounted = false;
307
- renderProgress(Object.assign({ status: "starting", completed: 0, failed: 0, extracted: 0 }, started));
308
- await pollProgress();
293
+ started = await postJson("/migrate", { conversationKeys: selectedKeys });
309
294
  } catch (e) {
310
295
  decisionMade = false;
311
296
  renderDashboard(routeError(e && e.message));
297
+ return;
298
+ }
299
+ extractMounted = false;
300
+ renderProgress(Object.assign({ status: "starting", completed: 0, failed: 0, extracted: 0 }, started));
301
+ await pollProgress();
302
+ }
303
+ async function resumeExistingExtraction() {
304
+ try {
305
+ var existing = await getJson("/progress");
306
+ if (!existing || existing.status === "idle") return false;
307
+ decisionMade = true;
308
+ extractionEnded = false;
309
+ extractMounted = false;
310
+ renderProgress(existing);
311
+ if (existing.status !== "completed" && existing.status !== "failed") {
312
+ void pollProgress();
313
+ }
314
+ return true;
315
+ } catch (_) {
316
+ // Stats cannot prove that no import is running. Stay in the recovery view until
317
+ // the authoritative progress endpoint answers instead of exposing a second start.
318
+ decisionMade = true;
319
+ extractionEnded = false;
320
+ extractMounted = false;
321
+ renderProgress({
322
+ status: "running",
323
+ total: 0,
324
+ completed: 0,
325
+ failed: 0,
326
+ extracted: 0,
327
+ latest: "Reconnecting to the active extraction..."
328
+ });
329
+ void pollProgress();
330
+ return true;
312
331
  }
313
332
  }
314
333
  async function pollProgress() {
315
334
  var runId = ++progressPollRunId;
335
+ var failures = 0;
316
336
  for (;;) {
317
337
  if (extractionEnded || runId !== progressPollRunId) return;
318
- var progress = await getJson("/progress");
338
+ var progress;
339
+ try {
340
+ progress = await getJson("/progress");
341
+ failures = 0;
342
+ } catch (_) {
343
+ failures++;
344
+ var reconnecting = Object.assign({
345
+ status: "running",
346
+ total: 0,
347
+ completed: 0,
348
+ failed: 0,
349
+ extracted: 0
350
+ }, lastExtractionProgress || {});
351
+ reconnecting.status = reconnecting.status === "completed" || reconnecting.status === "failed"
352
+ ? "running"
353
+ : reconnecting.status;
354
+ reconnecting.latest = "Connection interrupted. Reconnecting to the active extraction...";
355
+ renderProgress(reconnecting);
356
+ await sleep(Math.min(5000, 500 * Math.pow(2, Math.min(failures - 1, 3))));
357
+ continue;
358
+ }
319
359
  if (extractionEnded || runId !== progressPollRunId) return;
320
360
  renderProgress(progress);
321
361
  if (progress.status === "completed" || progress.status === "failed") return;
@@ -803,7 +843,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
803
843
  }
804
844
  async function switchSetupAccount() {
805
845
  var button = document.getElementById("changeSetupAccount");
806
- if (button) { button.disabled = true; button.textContent = "Opening account chooser…"; }
846
+ if (button) { button.disabled = true; button.textContent = "Signing out..."; }
807
847
  try {
808
848
  await postJson("/logout", {}, 12000);
809
849
  stats = null;
@@ -811,15 +851,13 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
811
851
  connected = false;
812
852
  selectedSessionKeys = Object.create(null);
813
853
  sessionSelectionTouched = false;
814
- if (!openAuthWindow(switchAccountUrl || authUrl)) {
815
- if (button) { button.disabled = false; button.textContent = "Change account"; }
816
- showPopupFallback("Your browser blocked the account chooser. Open it below to continue.", switchAccountUrl || authUrl);
817
- return;
818
- }
819
- startConnectionPoll();
854
+ localAuthEmail = "";
855
+ localAuthTermsAccepted = false;
856
+ localAuthAgeConfirmed = false;
857
+ renderLocalLogin("");
820
858
  } catch (error) {
821
859
  if (button) { button.disabled = false; button.textContent = "Change account"; }
822
- setSetupPlanStatus(error && error.message ? error.message : "Could not open the account chooser.");
860
+ setSetupPlanStatus(error && error.message ? error.message : "Could not switch accounts.");
823
861
  }
824
862
  }
825
863
  function paidRecallPlan(plan) {
@@ -884,7 +922,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
884
922
  '<div class="setupPlanHead"><strong>Pro</strong><span>$40 / month</span></div>' +
885
923
  '<p>More recall for serious work.</p>' +
886
924
  '<ul><li>' + setupIcon("check") + '<span>250 bulk history imports</span></li><li>' + setupIcon("check") + '<span>200K new-chat tokens each week</span></li><li>' + setupIcon("check") + '<span>100 memory searches each week</span></li><li>' + setupIcon("check") + '<span>No memory-count limit</span></li></ul>' +
887
- '<button type="button" class="primary" id="chooseProPlan">' + (trialAvailable ? "Start 7-day trial" : "Choose Pro") + '</button>' +
925
+ '<button type="button" class="primary" id="chooseProPlan">' + (trialAvailable ? "Start 14-day trial" : "Choose Pro") + '</button>' +
888
926
  '<small>' + (trialAvailable ? "Card required. Cancel anytime." : "Your trial was already used.") + '</small>' +
889
927
  '</article>' +
890
928
  '<article class="setupPlanCard">' +
@@ -892,7 +930,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
892
930
  '<p>Higher limits for people who use Echo every day.</p>' +
893
931
  // Future Power feature: API and automation access.
894
932
  '<ul><li>' + setupIcon("check") + '<span>500 bulk history imports</span></li><li>' + setupIcon("check") + '<span>750K new-chat tokens each week</span></li><li>' + setupIcon("check") + '<span>250 memory searches each week</span></li><li>' + setupIcon("check") + '<span>No memory-count limit</span></li></ul>' +
895
- '<button type="button" class="secondary" id="choosePowerPlan">' + (trialAvailable ? "Start 7-day trial" : "Choose Power") + '</button>' +
933
+ '<button type="button" class="secondary" id="choosePowerPlan">' + (trialAvailable ? "Start 14-day trial" : "Choose Power") + '</button>' +
896
934
  '<small>' + (trialAvailable ? "Card required. Cancel anytime." : "Your trial was already used.") + '</small>' +
897
935
  '</article>' +
898
936
  '</div>' +
@@ -137,13 +137,11 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
137
137
  var data = event.data || {};
138
138
  if (data.type === "echomem:connected" && data.nonce === nonce) {
139
139
  if (!localHistoryConsentGranted) {
140
- closeAuthWindow();
141
140
  renderLocalScanConsent();
142
141
  setConsentStatus("Local history access is required before you can connect EchoMem or continue setup.", "required");
143
142
  return;
144
143
  }
145
144
  connected = true;
146
- closeAuthWindow();
147
145
  void waitForStats();
148
146
  } else if (data.type === "echomem:city-ready" && data.nonce === nonce) {
149
147
  scheduleReportToCity();
@@ -159,12 +157,10 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
159
157
  var configLoaded = false;
160
158
  var consentRequired = true;
161
159
  var consentGranted = false;
162
- // Learn the auth URL, connection state, and server-side consent gate before routing.
160
+ // Learn the connection state and server-side consent gate before routing.
163
161
  try {
164
162
  var cfg = await getJson("/config");
165
163
  configLoaded = true;
166
- authUrl = cfg.authUrl || authUrl;
167
- switchAccountUrl = cfg.switchAccountUrl || switchAccountUrl || authUrl;
168
164
  connected = !!cfg.connected;
169
165
  workspacePath = cfg.workspacePath || workspacePath;
170
166
  consentRequired = cfg.consentRequired !== false;
@@ -139,6 +139,129 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
139
139
  font-size: 12px;
140
140
  font-weight: 650;
141
141
  }
142
+ .localAuthStage {
143
+ display: grid;
144
+ place-items: center;
145
+ width: min(720px, 100%);
146
+ min-height: min(660px, calc(100vh - 120px));
147
+ margin: 0 auto;
148
+ }
149
+ .localAuthCard {
150
+ width: min(680px, 100%);
151
+ border: 1.5px solid rgba(26,58,143,0.16);
152
+ border-radius: 20px;
153
+ background: rgba(255,255,255,0.88);
154
+ padding: clamp(22px, 4vw, 34px);
155
+ box-shadow: 0 24px 60px -42px rgba(20,36,84,0.48);
156
+ text-align: left;
157
+ }
158
+ .localAuthLead {
159
+ display: grid;
160
+ justify-items: start;
161
+ gap: 10px;
162
+ }
163
+ .localAuthLead img {
164
+ width: 58px;
165
+ height: 58px;
166
+ object-fit: contain;
167
+ filter: drop-shadow(0 10px 16px rgba(26,58,143,0.18));
168
+ }
169
+ .localAuthLead h2 {
170
+ max-width: 610px;
171
+ font-size: clamp(30px, 4vw, 46px);
172
+ line-height: 1;
173
+ }
174
+ .localAuthLead p:not(.consentEyebrow) {
175
+ max-width: 590px;
176
+ margin: 0;
177
+ color: var(--echo-ink-mute);
178
+ font-size: 15px;
179
+ line-height: 1.55;
180
+ }
181
+ .localAuthForm {
182
+ display: grid;
183
+ gap: 14px;
184
+ margin-top: 22px;
185
+ }
186
+ .localAuthField {
187
+ display: grid;
188
+ gap: 7px;
189
+ }
190
+ .localAuthField > span {
191
+ font-family: var(--echo-font-mono);
192
+ color: var(--echo-ink-mute);
193
+ font-size: 11px;
194
+ font-weight: 800;
195
+ letter-spacing: 0.06em;
196
+ text-transform: uppercase;
197
+ }
198
+ .localAuthField input {
199
+ width: 100%;
200
+ min-height: 50px;
201
+ border: 1px solid rgba(26,58,143,0.20);
202
+ border-radius: 10px;
203
+ background: #fff;
204
+ color: var(--echo-ink-text);
205
+ padding: 0 14px;
206
+ font: inherit;
207
+ font-size: 16px;
208
+ outline: none;
209
+ }
210
+ .localAuthField input:focus {
211
+ border-color: var(--echo-ink-primary);
212
+ box-shadow: 0 0 0 3px rgba(26,58,143,0.12);
213
+ }
214
+ .localAuthLegal {
215
+ display: grid;
216
+ gap: 10px;
217
+ border: 1px solid rgba(26,58,143,0.10);
218
+ border-radius: 14px;
219
+ background: rgba(240,239,233,0.56);
220
+ padding: 13px 14px;
221
+ }
222
+ .localAuthLegal label {
223
+ display: grid;
224
+ grid-template-columns: 18px minmax(0, 1fr);
225
+ gap: 10px;
226
+ align-items: start;
227
+ color: var(--echo-ink-mute);
228
+ font-size: 12px;
229
+ line-height: 1.45;
230
+ }
231
+ .localAuthLegal input {
232
+ width: 16px;
233
+ height: 16px;
234
+ margin: 2px 0 0;
235
+ accent-color: var(--echo-ink-primary);
236
+ }
237
+ .localAuthLegal a {
238
+ color: var(--echo-ink-primary);
239
+ font-weight: 750;
240
+ text-decoration: underline;
241
+ text-underline-offset: 2px;
242
+ }
243
+ .localAuthActions {
244
+ display: flex;
245
+ flex-wrap: wrap;
246
+ gap: 10px;
247
+ align-items: center;
248
+ }
249
+ .localAuthStatus {
250
+ min-height: 18px;
251
+ margin: 0;
252
+ color: var(--echo-ink-mute);
253
+ font-size: 13px;
254
+ line-height: 1.45;
255
+ }
256
+ .localAuthStatus:empty { display: none; }
257
+ .localAuthStatus.is-error {
258
+ border: 1px solid rgba(139,44,38,0.22);
259
+ border-radius: 10px;
260
+ background: #fff4ee;
261
+ padding: 9px 11px;
262
+ color: #8b2c26;
263
+ font-weight: 650;
264
+ }
142
265
  .track { height: 12px; overflow: hidden; border-radius: 999px; background: #e8e8e1; margin: 16px 0 10px; }
143
266
  .fill { height: 100%; width: 0%; background: var(--echo-ink-primary); transition: width 180ms ease; }
144
267
  /* indeterminate "we're working on it" bar for the prep phase (before job counts move) */
@@ -173,6 +296,8 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
173
296
  .explainCta .promiseActions { width: 100%; }
174
297
  .explainCta .primary, .explainCta .secondary { width: 100%; }
175
298
  .consentActions .primary { width: 100%; }
299
+ .localAuthActions .primary,
300
+ .localAuthForm > .primary { width: 100%; }
176
301
  .cityHero { min-height: auto; }
177
302
  .cityShell {
178
303
  width: 100vw;
@@ -1,5 +1,6 @@
1
1
  import { renderSetupPageDocument } from "./setup-page/document.js";
2
2
  import { renderSetupPreviewBootstrap } from "./setup-preview.js";
3
+ export { SETUP_PREVIEW_STATES } from "./setup-preview.js";
3
4
  /**
4
5
  * Setup page entry point. The implementation is split by product phase under ./setup-page/:
5
6
  * core utilities, local report, post-auth extraction, and lifecycle polling.
@@ -5,7 +5,18 @@ export const SETUP_PREVIEW_STATES = [
5
5
  "report",
6
6
  "extract-counting",
7
7
  "extract-ready",
8
+ "extract-free-selected",
9
+ "extract-free-trial-used",
8
10
  "extract-limit",
11
+ "extract-pro-trial",
12
+ "extract-pro-active",
13
+ "extract-power-active",
14
+ "extract-team-active",
15
+ "extract-enterprise-active",
16
+ "extract-unknown",
17
+ "auth-login",
18
+ "auth-unlock",
19
+ "auth-setup",
9
20
  "extract-run",
10
21
  "extract-overdue",
11
22
  "extract-done",
@@ -202,6 +213,62 @@ function previewWatermarkScript() {
202
213
  document.body.appendChild(previewWatermark);
203
214
  `;
204
215
  }
216
+ function extractionPreviewBootstrap(options) {
217
+ const candidateCount = options.candidateCount ?? 520;
218
+ const quotaLimit = options.quotaLimit;
219
+ const quotaRemaining = options.quotaRemaining;
220
+ const historicalConversationQuota = quotaLimit == null || quotaRemaining == null
221
+ ? null
222
+ : {
223
+ used: Math.max(0, quotaLimit - quotaRemaining),
224
+ limit: quotaLimit,
225
+ remaining: quotaRemaining,
226
+ };
227
+ const billingStatus = {
228
+ plan: options.plan,
229
+ paid: options.paid,
230
+ trialAvailable: options.trialAvailable,
231
+ trialUsed: options.trialUsed,
232
+ subscriptionStatus: options.subscriptionStatus ?? null,
233
+ trialEndedAt: options.trialEndedAt ?? null,
234
+ pricingUrl: "https://echoknows.com/pricing?source=mcp_onboarding",
235
+ account: {
236
+ displayName: "Preview User",
237
+ email: "preview@example.com",
238
+ avatarUrl: "/hud-assets/echo-face-cutout.png",
239
+ },
240
+ historicalConversationQuota,
241
+ };
242
+ return `
243
+ connected = true;
244
+ billingStatus = ${JSON.stringify(billingStatus)};
245
+ setupPlanChoice = ${JSON.stringify(options.selectFree ? "free" : "")};
246
+ stats = {
247
+ sessions: { total: ${candidateCount}, codex: ${Math.ceil(candidateCount * 0.68)}, claudeCode: ${Math.floor(candidateCount * 0.32)} },
248
+ migratable: {
249
+ pending: ${candidateCount}, pendingTotal: ${candidateCount}, pendingCodex: ${Math.ceil(candidateCount * 0.68)}, pendingClaudeCode: ${Math.floor(candidateCount * 0.32)},
250
+ alreadyMigrated: 108, skippedActive: 0,
251
+ eta: { estimatedLabel: "under 2 minutes" }, estimatedLabel: "under 2 minutes"
252
+ },
253
+ candidateSessions: Array.from({ length: ${candidateCount} }, function (_, index) {
254
+ var codex = index < ${Math.ceil(candidateCount * 0.68)};
255
+ return {
256
+ key: (codex ? "codex:" : "claude-code:") + "preview-" + index,
257
+ source: codex ? "codex" : "claude-code",
258
+ title: ["Plan the onboarding flow", "Repair memory extraction", "Review pricing limits", "Polish the EchoMem HUD"][index % 4] + " · " + (index + 1),
259
+ project: ["EchoMem Chrome", "MCP Server", "Memory Platform"][index % 3],
260
+ date: new Date(Date.UTC(2026, 6, 18 - (index % 12))).toISOString(),
261
+ characters: 42000 + index * 2700,
262
+ approxInputTokens: 10500 + index * 675,
263
+ turns: 18 + index
264
+ };
265
+ }),
266
+ discovery: { phase: "account", exact: false }
267
+ };
268
+ renderDashboard();
269
+ ${options.showUnknownStatus ? 'setSetupPlanStatus("Plan check unavailable. Free still works.");' : ""}
270
+ ${options.openSessionPicker ? "window.setTimeout(function () { openSessionPicker(); }, 0);" : ""}`;
271
+ }
205
272
  export function renderSetupPreviewBootstrap(state) {
206
273
  const watermark = previewWatermarkScript();
207
274
  if (state === "consent") {
@@ -236,44 +303,66 @@ export function renderSetupPreviewBootstrap(state) {
236
303
  stats = null;
237
304
  renderDashboard();`;
238
305
  }
239
- if (state === "extract-ready" || state === "extract-limit") {
240
- const remainingHistory = state === "extract-limit" ? 0 : 28;
241
- return `${watermark}
242
- connected = true;
243
- billingStatus = {
244
- plan: "free", paid: false, trialAvailable: true,
245
- pricingUrl: "https://echoknows.com/pricing?source=mcp_onboarding",
246
- account: {
247
- displayName: "Erik Hein",
248
- email: "erik@example.com",
249
- avatarUrl: "/hud-assets/echo-face-cutout.png"
250
- },
251
- historicalConversationQuota: { used: ${100 - remainingHistory}, limit: 100, remaining: ${remainingHistory} }
252
- };
253
- stats = {
254
- sessions: { total: 136, codex: 82, claudeCode: 54 },
255
- migratable: {
256
- pending: 28, pendingTotal: 28, pendingCodex: 19, pendingClaudeCode: 9,
257
- alreadyMigrated: 108, skippedActive: 0,
258
- eta: { estimatedLabel: "under 2 minutes" }, estimatedLabel: "under 2 minutes"
259
- },
260
- candidateSessions: Array.from({ length: 28 }, function (_, index) {
261
- var codex = index < 19;
262
- return {
263
- key: (codex ? "codex:" : "claude-code:") + "preview-" + index,
264
- source: codex ? "codex" : "claude-code",
265
- title: ["Plan the onboarding flow", "Repair memory extraction", "Review pricing limits", "Polish the EchoMem HUD"][index % 4] + " · " + (index + 1),
266
- project: ["EchoMem Chrome", "MCP Server", "Memory Platform"][index % 3],
267
- date: new Date(Date.UTC(2026, 6, 18 - (index % 12))).toISOString(),
268
- characters: 42000 + index * 2700,
269
- approxInputTokens: 10500 + index * 675,
270
- turns: 18 + index
271
- };
272
- }),
273
- discovery: { phase: "account", exact: false }
274
- };
275
- renderDashboard();`;
276
- }
306
+ if (state === "extract-ready")
307
+ return `${watermark}${extractionPreviewBootstrap({
308
+ plan: "free", paid: false, trialAvailable: true, trialUsed: false,
309
+ quotaLimit: 100, quotaRemaining: 28, candidateCount: 28,
310
+ })}`;
311
+ if (state === "extract-free-selected")
312
+ return `${watermark}${extractionPreviewBootstrap({
313
+ plan: "free", paid: false, trialAvailable: true, trialUsed: false,
314
+ quotaLimit: 100, quotaRemaining: 100, selectFree: true,
315
+ })}`;
316
+ if (state === "extract-free-trial-used")
317
+ return `${watermark}${extractionPreviewBootstrap({
318
+ plan: "free", paid: false, trialAvailable: false, trialUsed: true,
319
+ subscriptionStatus: "expired", trialEndedAt: "2026-07-20T00:00:00.000Z",
320
+ quotaLimit: 100, quotaRemaining: 100,
321
+ })}`;
322
+ if (state === "extract-limit")
323
+ return `${watermark}${extractionPreviewBootstrap({
324
+ plan: "free", paid: false, trialAvailable: false, trialUsed: true,
325
+ quotaLimit: 100, quotaRemaining: 0, selectFree: true, openSessionPicker: true,
326
+ })}`;
327
+ if (state === "extract-pro-trial")
328
+ return `${watermark}${extractionPreviewBootstrap({
329
+ plan: "pro", paid: true, trialAvailable: false, trialUsed: true,
330
+ subscriptionStatus: "trialing", trialEndedAt: "2026-08-04T00:00:00.000Z",
331
+ // The server currently grants trialing users Free's lifetime history allowance,
332
+ // even though recurring processing/search allowances resolve to Pro.
333
+ quotaLimit: 100, quotaRemaining: 100,
334
+ })}`;
335
+ if (state === "extract-pro-active")
336
+ return `${watermark}${extractionPreviewBootstrap({
337
+ plan: "pro", paid: true, trialAvailable: false, trialUsed: true,
338
+ subscriptionStatus: "active", quotaLimit: 250, quotaRemaining: 250,
339
+ })}`;
340
+ if (state === "extract-power-active")
341
+ return `${watermark}${extractionPreviewBootstrap({
342
+ plan: "power", paid: true, trialAvailable: false, trialUsed: true,
343
+ subscriptionStatus: "active", quotaLimit: 500, quotaRemaining: 500,
344
+ })}`;
345
+ if (state === "extract-team-active")
346
+ return `${watermark}${extractionPreviewBootstrap({
347
+ plan: "team", paid: true, trialAvailable: false, trialUsed: true,
348
+ subscriptionStatus: "active", quotaLimit: 250, quotaRemaining: 250,
349
+ })}`;
350
+ if (state === "extract-enterprise-active")
351
+ return `${watermark}${extractionPreviewBootstrap({
352
+ plan: "enterprise", paid: true, trialAvailable: false, trialUsed: true,
353
+ subscriptionStatus: "active", quotaLimit: 500, quotaRemaining: 500,
354
+ })}`;
355
+ if (state === "extract-unknown")
356
+ return `${watermark}${extractionPreviewBootstrap({
357
+ plan: "unknown", paid: false, trialAvailable: true, trialUsed: false,
358
+ showUnknownStatus: true,
359
+ })}`;
360
+ if (state === "auth-login")
361
+ return `${watermark}\n renderLocalLogin("");`;
362
+ if (state === "auth-unlock")
363
+ return `${watermark}\n renderLocalPassphrase("unlock", "preview@example.com");`;
364
+ if (state === "auth-setup")
365
+ return `${watermark}\n renderLocalPassphrase("setup", "preview@example.com");`;
277
366
  if (state === "extract-run") {
278
367
  return `${watermark}
279
368
  renderProgress({ status: "running", total: 28, completed: 4, extracted: 19, failed: 1, latest: "preview session", latestRepo: "Preview Workspace" });`;