@ait-co/devtools 0.1.101 → 0.1.103

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.
Files changed (67) hide show
  1. package/README.en.md +0 -27
  2. package/README.md +0 -27
  3. package/dist/{chii-relay-CUS9FJKB.js → chii-relay-DkTOopRj.js} +1 -1
  4. package/dist/{chii-relay-CUS9FJKB.js.map → chii-relay-DkTOopRj.js.map} +1 -1
  5. package/dist/{chii-relay-BVVTS3tE.cjs → chii-relay-P6SKmB1g.cjs} +1 -1
  6. package/dist/{chii-relay-BVVTS3tE.cjs.map → chii-relay-P6SKmB1g.cjs.map} +1 -1
  7. package/dist/{deeplink-D1HXJ2YG.js → deeplink-B5-Hxu0Q.js} +1 -1
  8. package/dist/{deeplink-D1HXJ2YG.js.map → deeplink-B5-Hxu0Q.js.map} +1 -1
  9. package/dist/{deeplink-DDOe0FQl.cjs → deeplink-BzdbA1gV.cjs} +1 -1
  10. package/dist/{deeplink-DDOe0FQl.cjs.map → deeplink-BzdbA1gV.cjs.map} +1 -1
  11. package/dist/{devtools-opener-XpwL3fZ9.js → devtools-opener-B8nxrxqu.js} +2 -12
  12. package/dist/{devtools-opener-XpwL3fZ9.js.map → devtools-opener-B8nxrxqu.js.map} +1 -1
  13. package/dist/{devtools-opener-mDgeg_MX.cjs → devtools-opener-iv1OwfJN.cjs} +1 -1
  14. package/dist/{devtools-opener-mDgeg_MX.cjs.map → devtools-opener-iv1OwfJN.cjs.map} +1 -1
  15. package/dist/mcp/cli.js +20 -65
  16. package/dist/mcp/cli.js.map +1 -1
  17. package/dist/mcp/server.js +1 -1
  18. package/dist/panel/index.js +3 -751
  19. package/dist/panel/index.js.map +1 -1
  20. package/dist/{qr-http-server-Clvk1weS.cjs → qr-http-server-C9YPBo6H.cjs} +13 -58
  21. package/dist/qr-http-server-C9YPBo6H.cjs.map +1 -0
  22. package/dist/{qr-http-server-B1fmICC4.js → qr-http-server-Ck8o4PLI.js} +13 -58
  23. package/dist/qr-http-server-Ck8o4PLI.js.map +1 -0
  24. package/dist/{qr-http-server-ofopTUL-.js → qr-http-server-D09oMVit.js} +13 -58
  25. package/dist/qr-http-server-D09oMVit.js.map +1 -0
  26. package/dist/{qr-http-server-C9NUBysQ.cjs → qr-http-server-DegdwsSj.cjs} +13 -58
  27. package/dist/qr-http-server-DegdwsSj.cjs.map +1 -0
  28. package/dist/{relay-secret-store-J0SUUXjH.js → relay-secret-store-B0DH-8Qb.js} +46 -3
  29. package/dist/relay-secret-store-B0DH-8Qb.js.map +1 -0
  30. package/dist/{relay-secret-store-BvNWdSjV.js → relay-secret-store-Bns5rndt.js} +44 -3
  31. package/dist/relay-secret-store-Bns5rndt.js.map +1 -0
  32. package/dist/{relay-secret-store-B5WAozDv.cjs → relay-secret-store-I5q2Wvvv.cjs} +44 -3
  33. package/dist/relay-secret-store-I5q2Wvvv.cjs.map +1 -0
  34. package/dist/{relay-url-store-RKcao_yG.js → relay-url-store-BPeUZsiY.js} +2 -2
  35. package/dist/{relay-url-store-RKcao_yG.js.map → relay-url-store-BPeUZsiY.js.map} +1 -1
  36. package/dist/{relay-url-store-D2lX9POP.cjs → relay-url-store-CvmnevcO.cjs} +2 -2
  37. package/dist/{relay-url-store-D2lX9POP.cjs.map → relay-url-store-CvmnevcO.cjs.map} +1 -1
  38. package/dist/{relay-url-store-1CXVqNDL.js → relay-url-store-DJHZjk8o.js} +2 -2
  39. package/dist/{relay-url-store-1CXVqNDL.js.map → relay-url-store-DJHZjk8o.js.map} +1 -1
  40. package/dist/{totp-D9fjaVak.cjs → totp-CNw0w89F.cjs} +1 -1
  41. package/dist/{totp-D9fjaVak.cjs.map → totp-CNw0w89F.cjs.map} +1 -1
  42. package/dist/{totp-CauHjkdE.js → totp-DYdP9N3o.js} +1 -1
  43. package/dist/{totp-CauHjkdE.js.map → totp-DYdP9N3o.js.map} +1 -1
  44. package/dist/{tunnel-BmDfjkQI.cjs → tunnel-3RCjGaND.cjs} +6 -6
  45. package/dist/{tunnel-BmDfjkQI.cjs.map → tunnel-3RCjGaND.cjs.map} +1 -1
  46. package/dist/{tunnel-C_qpse3-.js → tunnel-qB2Soaaz.js} +6 -6
  47. package/dist/{tunnel-C_qpse3-.js.map → tunnel-qB2Soaaz.js.map} +1 -1
  48. package/dist/unplugin/index.cjs +5 -90
  49. package/dist/unplugin/index.cjs.map +1 -1
  50. package/dist/unplugin/index.d.cts.map +1 -1
  51. package/dist/unplugin/index.d.ts.map +1 -1
  52. package/dist/unplugin/index.js +5 -90
  53. package/dist/unplugin/index.js.map +1 -1
  54. package/dist/unplugin/tunnel.cjs +1 -1
  55. package/dist/unplugin/tunnel.js +1 -1
  56. package/package.json +1 -1
  57. package/dist/machine-state-Chg_6SPq.js +0 -188
  58. package/dist/machine-state-Chg_6SPq.js.map +0 -1
  59. package/dist/machine-state-DOUweFsJ.cjs +0 -216
  60. package/dist/machine-state-DOUweFsJ.cjs.map +0 -1
  61. package/dist/qr-http-server-B1fmICC4.js.map +0 -1
  62. package/dist/qr-http-server-C9NUBysQ.cjs.map +0 -1
  63. package/dist/qr-http-server-Clvk1weS.cjs.map +0 -1
  64. package/dist/qr-http-server-ofopTUL-.js.map +0 -1
  65. package/dist/relay-secret-store-B5WAozDv.cjs.map +0 -1
  66. package/dist/relay-secret-store-BvNWdSjV.js.map +0 -1
  67. package/dist/relay-secret-store-J0SUUXjH.js.map +0 -1
@@ -25218,11 +25218,6 @@ const en = {
25218
25218
  "panel.tab.analytics": "Analytics",
25219
25219
  "panel.tab.storage": "Storage",
25220
25220
  "common.readOnly": "Read-only — mock responses are controlled at build time.",
25221
- "toast.consent.title": "Send anonymous usage stats?",
25222
- "toast.consent.body": "We collect anonymous events only, to improve the tool. You can turn this off anytime in the Environment tab.",
25223
- "toast.consent.learnMore": "Learn more",
25224
- "toast.consent.accept": "Yes, send",
25225
- "toast.consent.deny": "No, thanks",
25226
25221
  "env.section.platform": "Platform",
25227
25222
  "env.row.os": "OS",
25228
25223
  "env.row.appVersion": "App Version",
@@ -25239,27 +25234,6 @@ const en = {
25239
25234
  "env.value.iosSwipeGesture.enabled": "enabled",
25240
25235
  "env.value.iosSwipeGesture.disabled": "disabled",
25241
25236
  "env.hint.iosSwipeGesture": "Last value passed to setIosSwipeGestureEnabled. Switching Environment to toss lets a toss-gated guard toggle this.",
25242
- "env.telemetry.section": "Telemetry",
25243
- "env.telemetry.t0Row": "Anonymous usage signal (Tier 0)",
25244
- "env.telemetry.t0On": "On",
25245
- "env.telemetry.t0Off": "Off",
25246
- "env.telemetry.t0TurnOn": "Turn on",
25247
- "env.telemetry.t0TurnOff": "Turn off",
25248
- "env.telemetry.t0Desc": "Version + date only, no PII. Once per day. Helps improve the package.",
25249
- "env.telemetry.row": "Extended telemetry (Tier 1)",
25250
- "env.telemetry.on": "On",
25251
- "env.telemetry.off": "Off",
25252
- "env.telemetry.turnOn": "Turn on",
25253
- "env.telemetry.turnOff": "Turn off",
25254
- "env.telemetry.anonIdLabel": "anon_id: {value}",
25255
- "env.telemetry.anonIdNotSet": "(not yet set)",
25256
- "env.telemetry.anonIdCopyTitle": "Click to copy full anon_id",
25257
- "env.telemetry.deleteBtn": "Delete my data",
25258
- "env.telemetry.deleting": "Deleting…",
25259
- "env.telemetry.deleted": "Deleted",
25260
- "env.telemetry.deleteFailedRetry": "Delete failed (please retry)",
25261
- "env.telemetry.deleteFailed": "Delete failed",
25262
- "env.telemetry.privacyLink": "Privacy policy →",
25263
25237
  "env.section.language": "Language",
25264
25238
  "env.language.row": "Language",
25265
25239
  "env.language.ko": "한국어",
@@ -25464,11 +25438,6 @@ const ko = {
25464
25438
  "panel.tab.analytics": "Analytics",
25465
25439
  "panel.tab.storage": "Storage",
25466
25440
  "common.readOnly": "읽기 전용 — mock 응답은 빌드 타임에 고정됩니다.",
25467
- "toast.consent.title": "익명 사용 통계를 보낼까요?",
25468
- "toast.consent.body": "도구 개선을 위해 익명 이벤트만 수집해요. 언제든 환경 탭에서 끌 수 있어요.",
25469
- "toast.consent.learnMore": "더 알아보기",
25470
- "toast.consent.accept": "네, 보낼게요",
25471
- "toast.consent.deny": "아니요",
25472
25441
  "env.section.platform": "Platform",
25473
25442
  "env.row.os": "OS",
25474
25443
  "env.row.appVersion": "App Version",
@@ -25485,27 +25454,6 @@ const ko = {
25485
25454
  "env.value.iosSwipeGesture.enabled": "enabled",
25486
25455
  "env.value.iosSwipeGesture.disabled": "disabled",
25487
25456
  "env.hint.iosSwipeGesture": "setIosSwipeGestureEnabled의 마지막 호출값. Environment를 toss로 바꾸면 toss-gated 가드가 이 값을 토글합니다.",
25488
- "env.telemetry.section": "Telemetry",
25489
- "env.telemetry.t0Row": "익명 사용 신호 (Tier 0)",
25490
- "env.telemetry.t0On": "On",
25491
- "env.telemetry.t0Off": "Off",
25492
- "env.telemetry.t0TurnOn": "Turn on",
25493
- "env.telemetry.t0TurnOff": "Turn off",
25494
- "env.telemetry.t0Desc": "버전·날짜만 수집, PII 없음. 하루 1회. 패키지 개선에 사용됩니다.",
25495
- "env.telemetry.row": "확장 텔레메트리 (Tier 1)",
25496
- "env.telemetry.on": "On",
25497
- "env.telemetry.off": "Off",
25498
- "env.telemetry.turnOn": "Turn on",
25499
- "env.telemetry.turnOff": "Turn off",
25500
- "env.telemetry.anonIdLabel": "anon_id: {value}",
25501
- "env.telemetry.anonIdNotSet": "(not yet set)",
25502
- "env.telemetry.anonIdCopyTitle": "전체 anon_id 복사",
25503
- "env.telemetry.deleteBtn": "내 데이터 삭제",
25504
- "env.telemetry.deleting": "삭제 중…",
25505
- "env.telemetry.deleted": "삭제 완료",
25506
- "env.telemetry.deleteFailedRetry": "삭제 실패 (다시 시도해주세요)",
25507
- "env.telemetry.deleteFailed": "삭제 실패",
25508
- "env.telemetry.privacyLink": "개인정보 처리방침 →",
25509
25457
  "env.section.language": "Language",
25510
25458
  "env.language.row": "Language",
25511
25459
  "env.language.ko": "한국어",
@@ -26061,621 +26009,6 @@ if (!globalRef[SINGLETON_KEY]) globalRef[SINGLETON_KEY] = new AitStateManager();
26061
26009
  const aitState = globalRef[SINGLETON_KEY];
26062
26010
  if (typeof window !== "undefined") window.__ait = aitState;
26063
26011
  //#endregion
26064
- //#region src/telemetry/consent-toast.ts
26065
- /**
26066
- * Consent toast UI — vanilla DOM, fixed bottom-right.
26067
- *
26068
- * Shows once per "undecided + reprompt window cleared" session.
26069
- * Calls onAccept / onDeny callbacks; caller is responsible for persisting state.
26070
- */
26071
- const TOAST_ID = "__ait-telemetry-toast";
26072
- const TOAST_STYLES = `
26073
- #${TOAST_ID} {
26074
- position: fixed;
26075
- z-index: 100001;
26076
- bottom: 80px;
26077
- right: 16px;
26078
- width: 280px;
26079
- background: #1a1a2e;
26080
- border: 1px solid #3a3a5a;
26081
- border-radius: 10px;
26082
- padding: 14px 16px;
26083
- box-shadow: 0 4px 24px rgba(0,0,0,0.4);
26084
- font-family: -apple-system, BlinkMacSystemFont, 'Pretendard', sans-serif;
26085
- font-size: 13px;
26086
- color: #e0e0e0;
26087
- box-sizing: border-box;
26088
- }
26089
- #${TOAST_ID} .ait-toast-header {
26090
- font-size: 13px;
26091
- font-weight: 600;
26092
- color: #e0e0e0;
26093
- margin-bottom: 6px;
26094
- }
26095
- #${TOAST_ID} .ait-toast-body {
26096
- font-size: 12px;
26097
- color: #aaa;
26098
- margin-bottom: 12px;
26099
- line-height: 1.5;
26100
- }
26101
- #${TOAST_ID} .ait-toast-actions {
26102
- display: flex;
26103
- gap: 8px;
26104
- align-items: center;
26105
- justify-content: flex-end;
26106
- }
26107
- #${TOAST_ID} .ait-toast-btn-primary {
26108
- background: #3182F6;
26109
- color: white;
26110
- border: none;
26111
- border-radius: 4px;
26112
- padding: 6px 12px;
26113
- font-size: 12px;
26114
- cursor: pointer;
26115
- font-family: inherit;
26116
- }
26117
- #${TOAST_ID} .ait-toast-btn-primary:hover { background: #1b6ef3; }
26118
- #${TOAST_ID} .ait-toast-btn-secondary {
26119
- background: #2a2a4a;
26120
- color: #e0e0e0;
26121
- border: 1px solid #3a3a5a;
26122
- border-radius: 4px;
26123
- padding: 6px 12px;
26124
- font-size: 12px;
26125
- cursor: pointer;
26126
- font-family: inherit;
26127
- }
26128
- #${TOAST_ID} .ait-toast-btn-secondary:hover { background: #3a3a5a; }
26129
- #${TOAST_ID} .ait-toast-link {
26130
- font-size: 11px;
26131
- color: #666;
26132
- text-decoration: none;
26133
- margin-right: auto;
26134
- }
26135
- #${TOAST_ID} .ait-toast-link:hover { color: #aaa; }
26136
- `;
26137
- function injectStyles() {
26138
- if (document.getElementById(`${TOAST_ID}-style`)) return;
26139
- const style = document.createElement("style");
26140
- style.id = `${TOAST_ID}-style`;
26141
- style.textContent = TOAST_STYLES;
26142
- document.head.appendChild(style);
26143
- }
26144
- function removeToast() {
26145
- document.getElementById(TOAST_ID)?.remove();
26146
- document.getElementById(`${TOAST_ID}-style`)?.remove();
26147
- }
26148
- /**
26149
- * Renders and shows the consent toast.
26150
- * If the toast is already visible, does nothing.
26151
- */
26152
- function showConsentToast({ onAccept, onDeny }) {
26153
- if (document.getElementById(TOAST_ID)) return;
26154
- injectStyles();
26155
- const toast = document.createElement("div");
26156
- toast.id = TOAST_ID;
26157
- const header = document.createElement("div");
26158
- header.className = "ait-toast-header";
26159
- header.textContent = t("toast.consent.title");
26160
- const body = document.createElement("div");
26161
- body.className = "ait-toast-body";
26162
- body.textContent = t("toast.consent.body");
26163
- const learnMore = document.createElement("a");
26164
- learnMore.className = "ait-toast-link";
26165
- learnMore.href = "https://docs.aitc.dev/privacy";
26166
- learnMore.target = "_blank";
26167
- learnMore.rel = "noopener noreferrer";
26168
- learnMore.textContent = t("toast.consent.learnMore");
26169
- const yesBtn = document.createElement("button");
26170
- yesBtn.className = "ait-toast-btn-primary";
26171
- yesBtn.textContent = t("toast.consent.accept");
26172
- yesBtn.addEventListener("click", () => {
26173
- removeToast();
26174
- onAccept();
26175
- });
26176
- const noBtn = document.createElement("button");
26177
- noBtn.className = "ait-toast-btn-secondary";
26178
- noBtn.textContent = t("toast.consent.deny");
26179
- noBtn.addEventListener("click", () => {
26180
- removeToast();
26181
- onDeny();
26182
- });
26183
- const actions = document.createElement("div");
26184
- actions.className = "ait-toast-actions";
26185
- actions.append(learnMore, noBtn, yesBtn);
26186
- toast.append(header, body, actions);
26187
- document.body.appendChild(toast);
26188
- }
26189
- //#endregion
26190
- //#region src/telemetry/state.ts
26191
- const KEY_CONSENT = "__ait_telemetry:consent";
26192
- const KEY_REPROMPT_AFTER = "__ait_telemetry:reprompt_after";
26193
- const KEY_POLICY_VERSION = "__ait_telemetry:policy_version";
26194
- const KEY_ANON_ID = "__ait_telemetry:anon_id";
26195
- const KEY_T0_LAST_SENT = "__ait_telemetry:t0_last_sent";
26196
- const KEY_T0_OFF = "__ait_telemetry:t0_off";
26197
- /**
26198
- * Returns true if Tier 0 ping is enabled.
26199
- * Disabled when `localStorage.__ait_telemetry:t0_off = '1'`
26200
- * or `process.env.AITC_TELEMETRY === 'off'`.
26201
- */
26202
- function isTier0Enabled() {
26203
- if (typeof process !== "undefined" && process.env.AITC_TELEMETRY === "off") return false;
26204
- try {
26205
- return localStorage.getItem(KEY_T0_OFF) !== "1";
26206
- } catch {
26207
- return true;
26208
- }
26209
- }
26210
- /**
26211
- * Sets or clears the Tier 0 opt-out marker.
26212
- */
26213
- function setTier0Enabled(enabled) {
26214
- try {
26215
- if (enabled) localStorage.removeItem(KEY_T0_OFF);
26216
- else localStorage.setItem(KEY_T0_OFF, "1");
26217
- } catch {}
26218
- }
26219
- /**
26220
- * Returns true if Tier 0 has already been sent today (YYYY-MM-DD).
26221
- */
26222
- function hasSentTier0Today() {
26223
- try {
26224
- const stored = localStorage.getItem(KEY_T0_LAST_SENT);
26225
- if (!stored) return false;
26226
- return stored === (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
26227
- } catch {
26228
- return false;
26229
- }
26230
- }
26231
- /**
26232
- * Records that Tier 0 was sent today.
26233
- */
26234
- function markTier0Sent() {
26235
- try {
26236
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
26237
- localStorage.setItem(KEY_T0_LAST_SENT, today);
26238
- } catch {}
26239
- }
26240
- /**
26241
- * Current policy version. Bump this string whenever the privacy policy changes.
26242
- * Users who previously granted on an older version will be re-prompted once.
26243
- */
26244
- const CURRENT_POLICY_VERSION = "2026-05-18";
26245
- /** 30 days in milliseconds */
26246
- const THIRTY_DAYS_MS = 720 * 60 * 60 * 1e3;
26247
- function readConsentState() {
26248
- const raw = localStorage.getItem(KEY_CONSENT);
26249
- if (raw === "granted" || raw === "denied") return raw;
26250
- return "undecided";
26251
- }
26252
- function readRepromptAfter() {
26253
- const raw = localStorage.getItem(KEY_REPROMPT_AFTER);
26254
- if (raw === null) return 0;
26255
- const n = Number(raw);
26256
- return Number.isFinite(n) ? n : 0;
26257
- }
26258
- function readPolicyVersion() {
26259
- return localStorage.getItem(KEY_POLICY_VERSION);
26260
- }
26261
- /**
26262
- * Returns the stored anon_id, or generates + persists a new UUID v4 on first call.
26263
- * Once generated it is never overwritten.
26264
- */
26265
- function getOrCreateAnonId() {
26266
- const existing = localStorage.getItem(KEY_ANON_ID);
26267
- if (existing) return existing;
26268
- const id = crypto.randomUUID();
26269
- localStorage.setItem(KEY_ANON_ID, id);
26270
- return id;
26271
- }
26272
- /**
26273
- * Resolve effective consent, handling the policy-version bump rule:
26274
- * - If stored = "granted" but stored version ≠ CURRENT → revert to undecided
26275
- * - If stored = "denied" and version changed → stay denied (no re-prompt)
26276
- *
26277
- * Call this at init time to normalise state before checking whether to show a toast.
26278
- * Returns the effective ConsentState after applying the version-bump rule.
26279
- */
26280
- function resolveEffectiveConsent() {
26281
- const raw = localStorage.getItem(KEY_CONSENT);
26282
- if (raw === "granted") {
26283
- if (readPolicyVersion() !== "2026-05-18") {
26284
- localStorage.removeItem(KEY_CONSENT);
26285
- localStorage.removeItem(KEY_POLICY_VERSION);
26286
- return "undecided";
26287
- }
26288
- return "granted";
26289
- }
26290
- if (raw === "denied") return "denied";
26291
- return "undecided";
26292
- }
26293
- /**
26294
- * User clicked "Yes, send".
26295
- * Sets consent = granted, records policy version.
26296
- */
26297
- function acceptConsent() {
26298
- localStorage.setItem(KEY_CONSENT, "granted");
26299
- localStorage.setItem(KEY_POLICY_VERSION, CURRENT_POLICY_VERSION);
26300
- localStorage.removeItem(KEY_REPROMPT_AFTER);
26301
- }
26302
- /**
26303
- * User clicked "No, thanks".
26304
- * First denial: sets reprompt_after = now + 30 days.
26305
- * Second denial (reprompt_after was already set to a past finite value that triggered
26306
- * re-prompt): sets reprompt_after = MAX_SAFE_INTEGER → permanent silence.
26307
- */
26308
- function denyConsent() {
26309
- localStorage.setItem(KEY_CONSENT, "denied");
26310
- const existing = readRepromptAfter();
26311
- if (existing > 0 && existing < Number.MAX_SAFE_INTEGER) localStorage.setItem(KEY_REPROMPT_AFTER, String(Number.MAX_SAFE_INTEGER));
26312
- else localStorage.setItem(KEY_REPROMPT_AFTER, String(Date.now() + THIRTY_DAYS_MS));
26313
- }
26314
- /**
26315
- * Environment-tab toggle: free transition between granted/denied.
26316
- * Does NOT touch reprompt_after.
26317
- */
26318
- function setConsentViaToggle(granted) {
26319
- if (granted) {
26320
- localStorage.setItem(KEY_CONSENT, "granted");
26321
- localStorage.setItem(KEY_POLICY_VERSION, CURRENT_POLICY_VERSION);
26322
- } else localStorage.setItem(KEY_CONSENT, "denied");
26323
- }
26324
- /**
26325
- * Returns true if the toast should be shown now.
26326
- * Conditions:
26327
- * - undecided (no prior choice or policy bumped to a newer version)
26328
- * - denied + reprompt_after set + reprompt_after < now (one re-prompt after
26329
- * the configured silence window; `denyConsent` flips to permanent silence
26330
- * on the second denial by setting reprompt_after to MAX_SAFE_INTEGER).
26331
- */
26332
- function shouldShowToast() {
26333
- const state = resolveEffectiveConsent();
26334
- if (state === "undecided") {
26335
- const repromptAfter = readRepromptAfter();
26336
- if (repromptAfter === 0) return true;
26337
- return Date.now() > repromptAfter;
26338
- }
26339
- if (state === "denied") {
26340
- const repromptAfter = readRepromptAfter();
26341
- if (repromptAfter === 0 || repromptAfter >= Number.MAX_SAFE_INTEGER) return false;
26342
- return Date.now() > repromptAfter;
26343
- }
26344
- return false;
26345
- }
26346
- /**
26347
- * Sends the DELETE request to remove the user's data from the server, and
26348
- * rotates the local anon_id on success so any subsequent events are unlinkable
26349
- * from the deleted history.
26350
- */
26351
- async function deleteMyData(endpoint) {
26352
- const anonId = localStorage.getItem(KEY_ANON_ID);
26353
- if (!anonId) return false;
26354
- try {
26355
- if (!(await fetch(`${endpoint}/e?anon_id=${encodeURIComponent(anonId)}`, { method: "DELETE" })).ok) return false;
26356
- localStorage.setItem(KEY_ANON_ID, crypto.randomUUID());
26357
- return true;
26358
- } catch {
26359
- return false;
26360
- }
26361
- }
26362
- //#endregion
26363
- //#region src/telemetry/send.ts
26364
- /**
26365
- * Telemetry send + retry.
26366
- *
26367
- * Rules:
26368
- * 1. If consent ≠ "granted" — drop silently.
26369
- * 2. POST event as JSON with 5 s timeout.
26370
- * 3. On network error or non-2xx: retry ONCE after 2 s. On second failure: drop.
26371
- * 4. console.debug on retry, development only (NODE_ENV !== "production").
26372
- * 5. For "session_duration": use sendBeacon if available, fall back to fetch keepalive.
26373
- *
26374
- * Max meta size: 256 bytes (JSON-serialized). Over-size meta is dropped to undefined.
26375
- */
26376
- /** Meta cap per server contract (JSON bytes). */
26377
- const META_BYTE_CAP = 256;
26378
- function sanitizeMeta(meta) {
26379
- if (meta === void 0) return void 0;
26380
- const serialized = JSON.stringify(meta);
26381
- if (new TextEncoder().encode(serialized).length > META_BYTE_CAP) return;
26382
- return meta;
26383
- }
26384
- async function doFetch(payload) {
26385
- const controller = new AbortController();
26386
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
26387
- try {
26388
- return (await fetch(`${TELEMETRY_ENDPOINT}/e`, {
26389
- method: "POST",
26390
- headers: { "Content-Type": "application/json" },
26391
- body: JSON.stringify(payload),
26392
- signal: controller.signal
26393
- })).ok;
26394
- } catch {
26395
- return false;
26396
- } finally {
26397
- clearTimeout(timeoutId);
26398
- }
26399
- }
26400
- function delay(ms) {
26401
- return new Promise((resolve) => setTimeout(resolve, ms));
26402
- }
26403
- /**
26404
- * Send a telemetry event. Drops silently if consent is not "granted".
26405
- */
26406
- async function send(event, version, meta) {
26407
- if (readConsentState() !== "granted") return;
26408
- const payload = {
26409
- tier: 1,
26410
- source: "devtools",
26411
- event,
26412
- anon_id: getOrCreateAnonId(),
26413
- version,
26414
- ts: Date.now(),
26415
- meta: sanitizeMeta(meta)
26416
- };
26417
- if (await doFetch(payload)) return;
26418
- if (process.env.NODE_ENV !== "production") console.debug("[@ait-co/devtools] telemetry: retrying after failure", event);
26419
- await delay(2e3);
26420
- await doFetch(payload);
26421
- }
26422
- /**
26423
- * Send the "session_duration" event via sendBeacon (unload-safe).
26424
- * Falls back to fetch with keepalive if sendBeacon is unavailable.
26425
- * No retry during page unload.
26426
- */
26427
- function sendBeaconEvent(event, version, meta) {
26428
- if (readConsentState() !== "granted") return;
26429
- const payload = {
26430
- tier: 1,
26431
- source: "devtools",
26432
- event,
26433
- anon_id: getOrCreateAnonId(),
26434
- version,
26435
- ts: Date.now(),
26436
- meta: sanitizeMeta(meta)
26437
- };
26438
- const body = JSON.stringify(payload);
26439
- if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
26440
- navigator.sendBeacon(`${TELEMETRY_ENDPOINT}/e`, new Blob([body], { type: "application/json" }));
26441
- return;
26442
- }
26443
- fetch(`${TELEMETRY_ENDPOINT}/e`, {
26444
- method: "POST",
26445
- headers: { "Content-Type": "application/json" },
26446
- body,
26447
- keepalive: true
26448
- }).catch(() => {});
26449
- }
26450
- //#endregion
26451
- //#region src/telemetry/tier0.ts
26452
- /**
26453
- * Tier 0 telemetry — opt-out, fire-and-forget daily ping.
26454
- *
26455
- * Payload: { tier: 0, source: 'devtools', ts: number, version: string }
26456
- * No anon_id. No event name. No meta.
26457
- *
26458
- * Rules:
26459
- * - Sent once per calendar day (localStorage daily marker).
26460
- * - Skipped when __ait_telemetry:t0_off = '1' or AITC_TELEMETRY=off.
26461
- * - 5 s timeout, no retry. Failure is silently dropped.
26462
- */
26463
- /**
26464
- * Sends the Tier 0 daily ping if eligible.
26465
- * Returns true if a ping was sent, false if skipped or failed.
26466
- */
26467
- async function sendTier0Ping(version) {
26468
- if (!isTier0Enabled()) return false;
26469
- if (hasSentTier0Today()) return false;
26470
- const payload = {
26471
- tier: 0,
26472
- source: "devtools",
26473
- ts: Date.now(),
26474
- version
26475
- };
26476
- const controller = new AbortController();
26477
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
26478
- try {
26479
- await fetch(`${TELEMETRY_ENDPOINT}/e`, {
26480
- method: "POST",
26481
- headers: { "Content-Type": "application/json" },
26482
- body: JSON.stringify(payload),
26483
- signal: controller.signal
26484
- });
26485
- markTier0Sent();
26486
- return true;
26487
- } catch {
26488
- return false;
26489
- } finally {
26490
- clearTimeout(timeoutId);
26491
- }
26492
- }
26493
- //#endregion
26494
- //#region src/telemetry/index.ts
26495
- /**
26496
- * Telemetry client — internal to @ait-co/devtools.
26497
- *
26498
- * NOT exported from src/mock/index.ts — this is panel-internal only.
26499
- *
26500
- * Usage: import { telemetry } from './telemetry/index.js' (from panel code).
26501
- */
26502
- /**
26503
- * The dev server consent endpoint path. Must match the path registered in
26504
- * the unplugin (`TELEMETRY_CONSENT_PATH`).
26505
- */
26506
- const MACHINE_CONSENT_ENDPOINT$1 = "/api/ait-devtools/telemetry-consent";
26507
- /**
26508
- * Whether we have already attempted to sync with the machine-level state.
26509
- * We sync once at init time to avoid repeated network calls.
26510
- */
26511
- let machineSyncDone = false;
26512
- /**
26513
- * Reads the machine-level consent state from the dev server endpoint.
26514
- * Returns `null` when not in a dev-server context (fetch fails, 503, non-JSON).
26515
- *
26516
- * BROWSER-ONLY. Runs inside the panel (browser context).
26517
- */
26518
- async function fetchMachineConsent() {
26519
- try {
26520
- const res = await fetch(MACHINE_CONSENT_ENDPOINT$1, {
26521
- method: "GET",
26522
- headers: { Accept: "application/json" }
26523
- });
26524
- if (!res.ok) return null;
26525
- const data = await res.json();
26526
- if (data.consent !== "granted" && data.consent !== "denied" && data.consent !== "undecided") return null;
26527
- return {
26528
- consent: data.consent,
26529
- anon_id: typeof data.anon_id === "string" ? data.anon_id : null,
26530
- policy_version: typeof data.policy_version === "string" ? data.policy_version : ""
26531
- };
26532
- } catch {
26533
- return null;
26534
- }
26535
- }
26536
- /**
26537
- * Posts a consent decision to the dev server so it is persisted to the
26538
- * machine-level file. Fire-and-forget — failures are silently ignored.
26539
- */
26540
- function postMachineConsent$1(consent, policyVersion) {
26541
- fetch(MACHINE_CONSENT_ENDPOINT$1, {
26542
- method: "POST",
26543
- headers: { "Content-Type": "application/json" },
26544
- body: JSON.stringify({
26545
- consent,
26546
- policy_version: policyVersion
26547
- })
26548
- }).catch(() => {});
26549
- }
26550
- /**
26551
- * Applies the machine-level consent to the browser localStorage so all
26552
- * subsequent reads from `state.ts` (which are locked to localStorage) reflect
26553
- * the machine decision.
26554
- *
26555
- * Only writes when the machine state is a definitive decision (granted/denied)
26556
- * and the policy version is current — avoids clobbering a more recent local
26557
- * decision with a stale machine file.
26558
- */
26559
- function applyMachineConsentToLocalStorage(machineState) {
26560
- if (machineState.consent === "undecided") return;
26561
- if (machineState.policy_version !== "2026-05-18") return;
26562
- setConsentViaToggle(machineState.consent === "granted");
26563
- if (machineState.anon_id) try {
26564
- if (!localStorage.getItem("__ait_telemetry:anon_id")) localStorage.setItem("__ait_telemetry:anon_id", machineState.anon_id);
26565
- } catch {}
26566
- }
26567
- /**
26568
- * Telemetry ingest endpoint.
26569
- * Overridable at build time via define (e.g., for e2e / local dev).
26570
- * Do NOT expose this as a public env-var surface.
26571
- */
26572
- function readGlobalString(key) {
26573
- const val = globalThis[key];
26574
- return typeof val === "string" ? val : void 0;
26575
- }
26576
- const TELEMETRY_ENDPOINT = readGlobalString("__TELEMETRY_ENDPOINT__") ?? "https://t.aitc.dev";
26577
- function getVersion() {
26578
- return "0.1.101";
26579
- }
26580
- let panelVisibleSince = null;
26581
- let accumulatedMs = 0;
26582
- let pagehideWired = false;
26583
- function onPanelVisible() {
26584
- if (panelVisibleSince === null) panelVisibleSince = Date.now();
26585
- }
26586
- function onPanelHidden() {
26587
- if (panelVisibleSince !== null) {
26588
- accumulatedMs += Date.now() - panelVisibleSince;
26589
- panelVisibleSince = null;
26590
- }
26591
- }
26592
- function wirePagehide() {
26593
- if (pagehideWired) return;
26594
- pagehideWired = true;
26595
- window.addEventListener("pagehide", () => {
26596
- onPanelHidden();
26597
- if (accumulatedMs > 0) sendBeaconEvent("session_duration", getVersion(), { ms: accumulatedMs });
26598
- });
26599
- }
26600
- /**
26601
- * Call once after panel mounts.
26602
- * Handles: Tier 0 ping, consent check (machine overlay first, localStorage
26603
- * fallback), optional toast, panel_mount event, pagehide wiring.
26604
- *
26605
- * Machine-level overlay (#542): when the dev server is running, we fetch the
26606
- * machine-level consent once and apply it to localStorage. This prevents the
26607
- * toast from re-appearing when the origin rotates (quick-tunnel host or port
26608
- * changes). On non-dev-server surfaces (GitHub Pages, static fixture) the
26609
- * fetch fails silently and we fall through to the existing localStorage path.
26610
- */
26611
- function init() {
26612
- if (typeof window === "undefined" || typeof document === "undefined") return;
26613
- wirePagehide();
26614
- sendTier0Ping(getVersion());
26615
- if (!machineSyncDone) {
26616
- machineSyncDone = true;
26617
- fetchMachineConsent().then((machineState) => {
26618
- if (machineState) applyMachineConsentToLocalStorage(machineState);
26619
- runConsentFlow();
26620
- });
26621
- } else runConsentFlow();
26622
- }
26623
- /**
26624
- * Core consent flow — reads localStorage (which may have already been
26625
- * patched by `applyMachineConsentToLocalStorage`) and shows the toast or
26626
- * fires `panel_mount` accordingly.
26627
- */
26628
- function runConsentFlow() {
26629
- if (resolveEffectiveConsent() === "granted") {
26630
- getOrCreateAnonId();
26631
- send("panel_mount", getVersion());
26632
- return;
26633
- }
26634
- if (shouldShowToast()) {
26635
- const showToast = () => {
26636
- showConsentToast({
26637
- onAccept: () => {
26638
- acceptConsent();
26639
- getOrCreateAnonId();
26640
- postMachineConsent$1("granted", CURRENT_POLICY_VERSION);
26641
- send("panel_mount", getVersion());
26642
- },
26643
- onDeny: () => {
26644
- denyConsent();
26645
- postMachineConsent$1("denied", CURRENT_POLICY_VERSION);
26646
- }
26647
- });
26648
- };
26649
- if (typeof requestIdleCallback === "function") requestIdleCallback(showToast, { timeout: 3e3 });
26650
- else setTimeout(showToast, 1500);
26651
- }
26652
- }
26653
- /**
26654
- * Call when the panel is opened/toggled visible.
26655
- */
26656
- function onPanelOpen() {
26657
- send("panel_open", getVersion());
26658
- onPanelVisible();
26659
- }
26660
- /**
26661
- * Call when the panel is closed/hidden.
26662
- */
26663
- function onPanelClose() {
26664
- onPanelHidden();
26665
- }
26666
- /**
26667
- * Call when the user switches tabs.
26668
- */
26669
- function onTabView(tabId) {
26670
- send("tab_view", getVersion(), { tab: tabId });
26671
- }
26672
- const telemetry = {
26673
- init,
26674
- onPanelOpen,
26675
- onPanelClose,
26676
- onTabView
26677
- };
26678
- //#endregion
26679
26012
  //#region node_modules/.pnpm/react@19.2.6/node_modules/react/cjs/react-jsx-runtime.production.js
26680
26013
  /**
26681
26014
  * @license React
@@ -28269,17 +27602,6 @@ function renderAnalyticsTab() {
28269
27602
  }
28270
27603
  //#endregion
28271
27604
  //#region src/panel/tabs/environment.ts
28272
- const MACHINE_CONSENT_ENDPOINT = "/api/ait-devtools/telemetry-consent";
28273
- function postMachineConsent(consent) {
28274
- fetch(MACHINE_CONSENT_ENDPOINT, {
28275
- method: "POST",
28276
- headers: { "Content-Type": "application/json" },
28277
- body: JSON.stringify({
28278
- consent,
28279
- policy_version: CURRENT_POLICY_VERSION
28280
- })
28281
- }).catch(() => {});
28282
- }
28283
27605
  function renderEnvironmentTab() {
28284
27606
  const s = aitState.state;
28285
27607
  const disabled = !s.panelEditable;
@@ -28294,7 +27616,7 @@ function renderEnvironmentTab() {
28294
27616
  "OFFLINE",
28295
27617
  "WWAN",
28296
27618
  "UNKNOWN"
28297
- ], s.networkStatus, (v) => aitState.update({ networkStatus: v }), disabled)), h("div", { className: "ait-section" }, h("div", { className: "ait-section-title" }, t("env.section.safeArea")), inputRow(t("env.row.safeArea.top"), String(s.safeAreaInsets.top), (v) => aitState.patch("safeAreaInsets", { top: Number(v) }), disabled), inputRow(t("env.row.safeArea.bottom"), String(s.safeAreaInsets.bottom), (v) => aitState.patch("safeAreaInsets", { bottom: Number(v) }), disabled)), buildNavigationSection(), buildLanguageSection(), buildTelemetrySection());
27619
+ ], s.networkStatus, (v) => aitState.update({ networkStatus: v }), disabled)), h("div", { className: "ait-section" }, h("div", { className: "ait-section-title" }, t("env.section.safeArea")), inputRow(t("env.row.safeArea.top"), String(s.safeAreaInsets.top), (v) => aitState.patch("safeAreaInsets", { top: Number(v) }), disabled), inputRow(t("env.row.safeArea.bottom"), String(s.safeAreaInsets.bottom), (v) => aitState.patch("safeAreaInsets", { bottom: Number(v) }), disabled)), buildNavigationSection(), buildLanguageSection());
28298
27620
  return container;
28299
27621
  }
28300
27622
  /**
@@ -28327,68 +27649,6 @@ function buildLanguageSection() {
28327
27649
  });
28328
27650
  return h("div", { className: "ait-section" }, h("div", { className: "ait-section-title" }, t("env.section.language")), h("div", { className: "ait-row" }, h("label", {}, t("env.language.row")), select));
28329
27651
  }
28330
- function buildTelemetrySection() {
28331
- const t0Enabled = isTier0Enabled();
28332
- const t0StatusLabel = h("span", { style: `font-size:12px;font-weight:600;color:${t0Enabled ? "#4ade80" : "#888"}` }, t0Enabled ? t("env.telemetry.t0On") : t("env.telemetry.t0Off"));
28333
- const t0ToggleBtn = h("button", {
28334
- className: "ait-btn ait-btn-sm",
28335
- style: "font-size:11px"
28336
- }, t0Enabled ? t("env.telemetry.t0TurnOff") : t("env.telemetry.t0TurnOn"));
28337
- t0ToggleBtn.addEventListener("click", () => {
28338
- setTier0Enabled(!t0Enabled);
28339
- window.dispatchEvent(new CustomEvent("__ait:panel-switch-tab", { detail: { tab: "env" } }));
28340
- });
28341
- const t0Row = h("div", { className: "ait-row" }, h("label", {}, t("env.telemetry.t0Row")), h("span", { style: "display:flex;align-items:center;gap:8px" }, t0StatusLabel, t0ToggleBtn));
28342
- const t0Desc = h("div", { style: "font-size:11px;color:#666;margin-bottom:6px" }, t("env.telemetry.t0Desc"));
28343
- const isGranted = readConsentState() === "granted";
28344
- const statusLabel = h("span", { style: `font-size:12px;font-weight:600;color:${isGranted ? "#4ade80" : "#888"}` }, isGranted ? t("env.telemetry.on") : t("env.telemetry.off"));
28345
- const toggleBtn = h("button", {
28346
- className: "ait-btn ait-btn-sm",
28347
- style: "font-size:11px"
28348
- }, isGranted ? t("env.telemetry.turnOff") : t("env.telemetry.turnOn"));
28349
- toggleBtn.addEventListener("click", () => {
28350
- const newConsent = !isGranted;
28351
- setConsentViaToggle(newConsent);
28352
- postMachineConsent(newConsent ? "granted" : "denied");
28353
- window.dispatchEvent(new CustomEvent("__ait:panel-switch-tab", { detail: { tab: "env" } }));
28354
- });
28355
- const statusRow = h("div", { className: "ait-row" }, h("label", {}, t("env.telemetry.row")), h("span", { style: "display:flex;align-items:center;gap:8px" }, statusLabel, toggleBtn));
28356
- const rawAnonId = localStorage.getItem("__ait_telemetry:anon_id");
28357
- const displayAnonId = rawAnonId ?? t("env.telemetry.anonIdNotSet");
28358
- const truncatedId = displayAnonId.length > 8 ? `${displayAnonId.slice(0, 8)}…` : displayAnonId;
28359
- const anonIdEl = h("span", {
28360
- style: "font-family:'SF Mono','Menlo',monospace;font-size:11px;color:#95e6cb;cursor:pointer",
28361
- title: t("env.telemetry.anonIdCopyTitle")
28362
- }, t("env.telemetry.anonIdLabel", { value: truncatedId }));
28363
- anonIdEl.addEventListener("click", () => {
28364
- if (!rawAnonId) return;
28365
- navigator.clipboard.writeText(rawAnonId).catch(() => {});
28366
- });
28367
- const deleteBtn = h("button", { className: "ait-btn ait-btn-sm ait-btn-danger" }, t("env.telemetry.deleteBtn"));
28368
- const deleteStatus = h("span", { style: "font-size:11px;color:#aaa" });
28369
- deleteBtn.addEventListener("click", () => {
28370
- deleteBtn.disabled = true;
28371
- deleteStatus.textContent = t("env.telemetry.deleting");
28372
- deleteMyData(TELEMETRY_ENDPOINT).then((ok) => {
28373
- deleteStatus.textContent = ok ? t("env.telemetry.deleted") : t("env.telemetry.deleteFailedRetry");
28374
- deleteBtn.disabled = false;
28375
- }).catch(() => {
28376
- deleteStatus.textContent = t("env.telemetry.deleteFailed");
28377
- deleteBtn.disabled = false;
28378
- });
28379
- });
28380
- const privacyLink = h("a", {
28381
- href: "https://docs.aitc.dev/privacy",
28382
- target: "_blank",
28383
- rel: "noopener noreferrer",
28384
- style: "font-size:11px;color:#666;text-decoration:none"
28385
- });
28386
- privacyLink.textContent = t("env.telemetry.privacyLink");
28387
- return h("div", { className: "ait-section" }, h("div", { className: "ait-section-title" }, t("env.telemetry.section")), t0Row, t0Desc, statusRow, h("div", { style: "margin-bottom:6px" }, anonIdEl), h("div", {
28388
- className: "ait-btn-row",
28389
- style: "align-items:center;gap:8px;margin-top:6px"
28390
- }, deleteBtn, deleteStatus), h("div", { style: "margin-top:8px" }, privacyLink));
28391
- }
28392
27652
  //#endregion
28393
27653
  //#region src/panel/tabs/events.ts
28394
27654
  function renderEventsTab() {
@@ -30776,12 +30036,7 @@ function Panel() {
30776
30036
  getPanelEl: () => panelRef.current,
30777
30037
  isOpenRef,
30778
30038
  onClickOnly: () => {
30779
- setIsOpen((prev) => {
30780
- const next = !prev;
30781
- if (next) telemetry.onPanelOpen();
30782
- else telemetry.onPanelClose();
30783
- return next;
30784
- });
30039
+ setIsOpen((prev) => !prev);
30785
30040
  }
30786
30041
  });
30787
30042
  (0, import_react.useEffect)(() => {
@@ -30805,7 +30060,6 @@ function Panel() {
30805
30060
  refresh();
30806
30061
  };
30807
30062
  window.addEventListener("__ait:panel-switch-tab", onSwitchTab);
30808
- telemetry.init();
30809
30063
  return () => {
30810
30064
  unsubscribe();
30811
30065
  window.removeEventListener("__ait:panel-switch-tab", onSwitchTab);
@@ -30816,7 +30070,6 @@ function Panel() {
30816
30070
  const editable = aitState.state.panelEditable;
30817
30071
  const onTabClick = (id) => {
30818
30072
  setCurrentTab(id);
30819
- telemetry.onTabView(id);
30820
30073
  refresh();
30821
30074
  };
30822
30075
  const onBadgeClick = () => {
@@ -30825,7 +30078,6 @@ function Panel() {
30825
30078
  };
30826
30079
  const onClose = () => {
30827
30080
  setIsOpen(false);
30828
- telemetry.onPanelClose();
30829
30081
  };
30830
30082
  const activeRenderer = renderersRef.current[currentTab];
30831
30083
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -30861,7 +30113,7 @@ function Panel() {
30861
30113
  color: "#666",
30862
30114
  fontWeight: 400
30863
30115
  },
30864
- children: ["v", "0.1.101"]
30116
+ children: ["v", "0.1.103"]
30865
30117
  }),
30866
30118
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
30867
30119
  type: "button",