canicode 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,204 @@ import { join, resolve, basename } from 'path';
10
10
  import { readFile } from 'fs/promises';
11
11
  import { homedir } from 'os';
12
12
 
13
+ var __defProp = Object.defineProperty;
14
+ var __getOwnPropNames = Object.getOwnPropertyNames;
15
+ var __esm = (fn, res) => function __init() {
16
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
17
+ };
18
+ var __export = (target, all) => {
19
+ for (var name in all)
20
+ __defProp(target, name, { get: all[name], enumerable: true });
21
+ };
22
+
23
+ // src/monitoring/browser.ts
24
+ var browser_exports = {};
25
+ __export(browser_exports, {
26
+ initBrowserMonitoring: () => initBrowserMonitoring,
27
+ shutdownBrowserMonitoring: () => shutdownBrowserMonitoring,
28
+ trackBrowserError: () => trackBrowserError,
29
+ trackBrowserEvent: () => trackBrowserEvent
30
+ });
31
+ function getGlobal() {
32
+ return globalThis;
33
+ }
34
+ function injectScript(src) {
35
+ return new Promise((resolve4, reject) => {
36
+ const g = getGlobal();
37
+ const doc = g["document"];
38
+ if (!doc) {
39
+ resolve4();
40
+ return;
41
+ }
42
+ const script = doc["createElement"]("script");
43
+ script["src"] = src;
44
+ script["async"] = true;
45
+ script["onload"] = () => resolve4();
46
+ script["onerror"] = () => reject(new Error(`Failed to load script: ${src}`));
47
+ doc["head"]["appendChild"](script);
48
+ });
49
+ }
50
+ async function initBrowserMonitoring(config2) {
51
+ if (config2.enabled === false) return;
52
+ const g = getGlobal();
53
+ if (!g["document"]) return;
54
+ monitoringEnabled = true;
55
+ if (config2.posthogApiKey) {
56
+ try {
57
+ await injectScript("https://us-assets.i.posthog.com/static/array.js");
58
+ g["posthog"]?.["init"]?.(config2.posthogApiKey, {
59
+ api_host: "https://us.i.posthog.com",
60
+ autocapture: false,
61
+ capture_pageview: true
62
+ });
63
+ } catch {
64
+ }
65
+ }
66
+ if (config2.sentryDsn) {
67
+ try {
68
+ await injectScript("https://browser.sentry-cdn.com/8.0.0/bundle.min.js");
69
+ g["Sentry"]?.["init"]?.({
70
+ dsn: config2.sentryDsn,
71
+ environment: config2.environment ?? "web",
72
+ release: config2.version,
73
+ tracesSampleRate: 0
74
+ });
75
+ } catch {
76
+ }
77
+ }
78
+ }
79
+ function trackBrowserEvent(event, properties) {
80
+ if (!monitoringEnabled) return;
81
+ try {
82
+ getGlobal()["posthog"]?.["capture"]?.(event, properties);
83
+ } catch {
84
+ }
85
+ }
86
+ function trackBrowserError(error, context) {
87
+ if (!monitoringEnabled) return;
88
+ try {
89
+ getGlobal()["Sentry"]?.["captureException"]?.(
90
+ error,
91
+ context ? { extra: context } : void 0
92
+ );
93
+ } catch {
94
+ }
95
+ try {
96
+ getGlobal()["posthog"]?.["capture"]?.("error", {
97
+ error: error.message,
98
+ ...context
99
+ });
100
+ } catch {
101
+ }
102
+ }
103
+ async function shutdownBrowserMonitoring() {
104
+ monitoringEnabled = false;
105
+ }
106
+ var monitoringEnabled;
107
+ var init_browser = __esm({
108
+ "src/monitoring/browser.ts"() {
109
+ monitoringEnabled = false;
110
+ }
111
+ });
112
+
113
+ // src/monitoring/node.ts
114
+ var node_exports = {};
115
+ __export(node_exports, {
116
+ initNodeMonitoring: () => initNodeMonitoring,
117
+ shutdownNodeMonitoring: () => shutdownNodeMonitoring,
118
+ trackNodeError: () => trackNodeError,
119
+ trackNodeEvent: () => trackNodeEvent
120
+ });
121
+ async function initNodeMonitoring(config2) {
122
+ if (config2.enabled === false) return;
123
+ monitoringEnabled2 = true;
124
+ commonProps = {
125
+ _sdk: "canicode",
126
+ _sdk_version: config2.version ?? "unknown",
127
+ _env: config2.environment ?? "unknown"
128
+ };
129
+ if (config2.posthogApiKey) {
130
+ try {
131
+ const mod = await import('posthog-node');
132
+ const PostHog = mod.PostHog;
133
+ posthogClient = new PostHog(config2.posthogApiKey, {
134
+ host: "https://us.i.posthog.com",
135
+ flushAt: 10,
136
+ flushInterval: 1e4
137
+ });
138
+ } catch {
139
+ }
140
+ }
141
+ if (config2.sentryDsn) {
142
+ try {
143
+ const mod = await import('@sentry/node');
144
+ sentryModule = mod;
145
+ sentryModule.init({
146
+ dsn: config2.sentryDsn,
147
+ environment: config2.environment ?? "cli",
148
+ release: config2.version,
149
+ tracesSampleRate: 0
150
+ });
151
+ } catch {
152
+ }
153
+ }
154
+ }
155
+ function trackNodeEvent(event, properties) {
156
+ if (!monitoringEnabled2 || !posthogClient) return;
157
+ try {
158
+ const captureOpts = {
159
+ distinctId: "anonymous",
160
+ event
161
+ };
162
+ captureOpts.properties = { ...commonProps, ...properties };
163
+ posthogClient.capture(captureOpts);
164
+ } catch {
165
+ }
166
+ }
167
+ function trackNodeError(error, context) {
168
+ if (!monitoringEnabled2) return;
169
+ try {
170
+ sentryModule?.captureException(error, context ? { extra: context } : void 0);
171
+ } catch {
172
+ }
173
+ try {
174
+ posthogClient?.capture({
175
+ distinctId: "anonymous",
176
+ event: "cic_error",
177
+ properties: { ...commonProps, error: error.message, ...context }
178
+ });
179
+ } catch {
180
+ }
181
+ }
182
+ async function shutdownNodeMonitoring() {
183
+ if (!monitoringEnabled2) return;
184
+ const tasks = [];
185
+ if (posthogClient) {
186
+ tasks.push(
187
+ posthogClient.shutdown().catch(() => {
188
+ })
189
+ );
190
+ }
191
+ if (sentryModule) {
192
+ tasks.push(
193
+ sentryModule.close(2e3).catch(() => {
194
+ })
195
+ );
196
+ }
197
+ await Promise.allSettled(tasks);
198
+ posthogClient = null;
199
+ sentryModule = null;
200
+ monitoringEnabled2 = false;
201
+ }
202
+ var posthogClient, sentryModule, monitoringEnabled2, commonProps;
203
+ var init_node = __esm({
204
+ "src/monitoring/node.ts"() {
205
+ posthogClient = null;
206
+ sentryModule = null;
207
+ monitoringEnabled2 = false;
208
+ commonProps = {};
209
+ }
210
+ });
13
211
  var CategorySchema = z.enum([
14
212
  "layout",
15
213
  "token",
@@ -1024,6 +1222,15 @@ function getReportsDir() {
1024
1222
  function ensureReportsDir() {
1025
1223
  ensureDir(REPORTS_DIR);
1026
1224
  }
1225
+ function getTelemetryEnabled() {
1226
+ return readConfig().telemetry !== false;
1227
+ }
1228
+ function getPosthogApiKey() {
1229
+ return process.env["POSTHOG_API_KEY"] ?? readConfig().posthogApiKey;
1230
+ }
1231
+ function getSentryDsn() {
1232
+ return process.env["SENTRY_DSN"] ?? readConfig().sentryDsn;
1233
+ }
1027
1234
 
1028
1235
  // src/core/loader.ts
1029
1236
  function isFigmaUrl(input) {
@@ -1713,6 +1920,77 @@ function createPromptBasedCheck(_cr) {
1713
1920
  };
1714
1921
  }
1715
1922
 
1923
+ // src/monitoring/events.ts
1924
+ var EVENT_PREFIX = "cic_";
1925
+ var EVENTS = {
1926
+ // Analysis
1927
+ ANALYSIS_STARTED: `${EVENT_PREFIX}analysis_started`,
1928
+ ANALYSIS_COMPLETED: `${EVENT_PREFIX}analysis_completed`,
1929
+ ANALYSIS_FAILED: `${EVENT_PREFIX}analysis_failed`,
1930
+ // Report
1931
+ REPORT_GENERATED: `${EVENT_PREFIX}report_generated`,
1932
+ COMMENT_POSTED: `${EVENT_PREFIX}comment_posted`,
1933
+ COMMENT_FAILED: `${EVENT_PREFIX}comment_failed`,
1934
+ // MCP
1935
+ MCP_TOOL_CALLED: `${EVENT_PREFIX}mcp_tool_called`,
1936
+ // CLI
1937
+ CLI_COMMAND: `${EVENT_PREFIX}cli_command`,
1938
+ CLI_INIT: `${EVENT_PREFIX}cli_init`
1939
+ };
1940
+
1941
+ // src/monitoring/index.ts
1942
+ var _trackEvent = () => {
1943
+ };
1944
+ var _trackError = () => {
1945
+ };
1946
+ var _shutdown = () => Promise.resolve();
1947
+ function isBrowser() {
1948
+ const g = globalThis;
1949
+ return typeof g["window"] !== "undefined" && typeof g["document"] !== "undefined";
1950
+ }
1951
+ async function initMonitoring(config2) {
1952
+ if (config2.enabled === false) return;
1953
+ if (!config2.posthogApiKey && !config2.sentryDsn) return;
1954
+ try {
1955
+ if (isBrowser()) {
1956
+ const { initBrowserMonitoring: initBrowserMonitoring2, trackBrowserEvent: trackBrowserEvent2, trackBrowserError: trackBrowserError2, shutdownBrowserMonitoring: shutdownBrowserMonitoring2 } = await Promise.resolve().then(() => (init_browser(), browser_exports));
1957
+ await initBrowserMonitoring2(config2);
1958
+ _trackEvent = trackBrowserEvent2;
1959
+ _trackError = trackBrowserError2;
1960
+ _shutdown = shutdownBrowserMonitoring2;
1961
+ } else {
1962
+ const { initNodeMonitoring: initNodeMonitoring2, trackNodeEvent: trackNodeEvent2, trackNodeError: trackNodeError2, shutdownNodeMonitoring: shutdownNodeMonitoring2 } = await Promise.resolve().then(() => (init_node(), node_exports));
1963
+ await initNodeMonitoring2(config2);
1964
+ _trackEvent = trackNodeEvent2;
1965
+ _trackError = trackNodeError2;
1966
+ _shutdown = shutdownNodeMonitoring2;
1967
+ }
1968
+ } catch {
1969
+ }
1970
+ }
1971
+ function trackEvent(event, properties) {
1972
+ try {
1973
+ _trackEvent(event, properties);
1974
+ } catch {
1975
+ }
1976
+ }
1977
+ function trackError(error, context) {
1978
+ try {
1979
+ _trackError(error, context);
1980
+ } catch {
1981
+ }
1982
+ }
1983
+ async function shutdownMonitoring() {
1984
+ try {
1985
+ await _shutdown();
1986
+ } catch {
1987
+ }
1988
+ }
1989
+
1990
+ // src/monitoring/keys.ts
1991
+ var POSTHOG_API_KEY = "phc_rBFeG140KqJLpUnlpYDEFgdMM6JozZeqQsf9twXf5Dq" ;
1992
+ var SENTRY_DSN = "https://80a836a8300b25f17ef5bbf23afb5b3a@o4511080656207872.ingest.us.sentry.io/4511080661319680" ;
1993
+
1716
1994
  // src/rules/excluded-names.ts
1717
1995
  var EXCLUDED_NAME_PATTERN = /(badge|close|dismiss|overlay|float|fab|dot|indicator|corner|decoration|tag|status|notification|icon|ico|image|asset|filter|dim|dimmed|bg|background|logo|avatar|divider|separator|nav|navigation|gnb|header|footer|sidebar|toolbar|modal|dialog|popup|toast|tooltip|dropdown|menu|sticky|spinner|loader|cursor|cta|chatbot|thumb|thumbnail|tabbar|tab-bar|statusbar|status-bar)/i;
1718
1996
  function isExcludedName(name) {
@@ -2916,6 +3194,7 @@ Typical flow with Figma MCP:
2916
3194
  customRulesPath: z.string().optional().describe("Path to custom rules JSON file")
2917
3195
  },
2918
3196
  async ({ designData, input, fileKey, fileName, token, preset, targetNodeId, configPath, customRulesPath }) => {
3197
+ trackEvent(EVENTS.MCP_TOOL_CALLED, { tool: "analyze" });
2919
3198
  try {
2920
3199
  let file;
2921
3200
  let nodeId;
@@ -2961,6 +3240,13 @@ Typical flow with Figma MCP:
2961
3240
  });
2962
3241
  const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2963
3242
  exec(`${openCmd} "${reportPath}"`);
3243
+ trackEvent(EVENTS.ANALYSIS_COMPLETED, {
3244
+ nodeCount: result.nodeCount,
3245
+ issueCount: result.issues.length,
3246
+ grade: scores.overall.grade,
3247
+ percentage: scores.overall.percentage,
3248
+ source: designData ? "mcp-data" : "url"
3249
+ });
2964
3250
  const issuesByRule = {};
2965
3251
  for (const issue of result.issues) {
2966
3252
  const id = issue.violation.ruleId;
@@ -2990,6 +3276,13 @@ Typical flow with Figma MCP:
2990
3276
  ]
2991
3277
  };
2992
3278
  } catch (error) {
3279
+ trackError(
3280
+ error instanceof Error ? error : new Error(String(error)),
3281
+ { tool: "analyze" }
3282
+ );
3283
+ trackEvent(EVENTS.ANALYSIS_FAILED, {
3284
+ error: error instanceof Error ? error.message : String(error)
3285
+ });
2993
3286
  return {
2994
3287
  content: [
2995
3288
  {
@@ -3084,9 +3377,24 @@ Use this when the user asks about customization, configuration, rule settings, o
3084
3377
  }
3085
3378
  );
3086
3379
  async function main() {
3380
+ const monitoringConfig = {
3381
+ environment: "mcp",
3382
+ version: pkg.version,
3383
+ enabled: getTelemetryEnabled()
3384
+ };
3385
+ const phKey = getPosthogApiKey() || POSTHOG_API_KEY;
3386
+ monitoringConfig.posthogApiKey = phKey;
3387
+ const sDsn = getSentryDsn() || SENTRY_DSN;
3388
+ monitoringConfig.sentryDsn = sDsn;
3389
+ await initMonitoring(monitoringConfig).catch(() => {
3390
+ });
3087
3391
  const transport = new StdioServerTransport();
3088
3392
  await server.connect(transport);
3089
3393
  }
3394
+ process.on("beforeExit", () => {
3395
+ shutdownMonitoring().catch(() => {
3396
+ });
3397
+ });
3090
3398
  main().catch(console.error);
3091
3399
  //# sourceMappingURL=server.js.map
3092
3400
  //# sourceMappingURL=server.js.map