@algolia/wizard 0.98.0 → 0.99.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.
Files changed (2) hide show
  1. package/dist/main.js +134 -29
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1581,28 +1581,38 @@ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, rm } f
1581
1581
  import { join as join4 } from "node:path";
1582
1582
  var isStepVisible = (s) => s.visible !== false;
1583
1583
  var stateFile = (workflowId) => join4(stateDir(), `state-${workflowId}.json`);
1584
+ var pendingWrite = Promise.resolve();
1584
1585
  function runAppId(state) {
1585
1586
  const appId = state.algoliaState.appId;
1586
1587
  return typeof appId === "string" && appId ? appId : void 0;
1587
1588
  }
1588
1589
  async function loadWorkflowState(workflowId) {
1590
+ const file = stateFile(workflowId);
1591
+ await pendingWrite;
1589
1592
  try {
1590
- const raw = await readFile2(stateFile(workflowId), "utf8");
1593
+ const raw = await readFile2(file, "utf8");
1591
1594
  return JSON.parse(raw);
1592
1595
  } catch {
1593
1596
  return null;
1594
1597
  }
1595
1598
  }
1599
+ function enqueueWrite(write) {
1600
+ const next = pendingWrite.then(write, write);
1601
+ pendingWrite = next.catch(() => void 0);
1602
+ return next;
1603
+ }
1596
1604
  async function saveWorkflowState(state) {
1597
- await mkdir2(stateDir(), { recursive: true });
1598
- await writeFile2(
1599
- stateFile(state.workflowId),
1600
- JSON.stringify(state, null, 2),
1601
- "utf8"
1602
- );
1605
+ const dir = stateDir();
1606
+ const file = stateFile(state.workflowId);
1607
+ const json = JSON.stringify(state, null, 2);
1608
+ return enqueueWrite(async () => {
1609
+ await mkdir2(dir, { recursive: true });
1610
+ await writeFile2(file, json, "utf8");
1611
+ });
1603
1612
  }
1604
1613
  async function clearWorkflowState(workflowId) {
1605
- await rm(stateFile(workflowId), { force: true });
1614
+ const file = stateFile(workflowId);
1615
+ return enqueueWrite(() => rm(file, { force: true }));
1606
1616
  }
1607
1617
 
1608
1618
  // src/lib/modelProfiles.ts
@@ -1701,10 +1711,45 @@ var COUNT_METRIC = 1;
1701
1711
  var GAUGE_METRIC = 3;
1702
1712
  var FIRST_ATTEMPT = 1;
1703
1713
  var NO_TOKENS2 = 0;
1714
+ var NO_COST = 0;
1704
1715
  var AGENT_TELEMETRY_CONTEXT = {
1705
1716
  appId: void 0,
1706
- workflowId: void 0
1717
+ workflowId: void 0,
1718
+ agentRunId: void 0,
1719
+ sessionCostUsd: NO_COST
1707
1720
  };
1721
+ function resetTelemetryContext() {
1722
+ AGENT_TELEMETRY_CONTEXT.appId = void 0;
1723
+ AGENT_TELEMETRY_CONTEXT.workflowId = void 0;
1724
+ AGENT_TELEMETRY_CONTEXT.agentRunId = void 0;
1725
+ AGENT_TELEMETRY_CONTEXT.sessionCostUsd = NO_COST;
1726
+ }
1727
+ function sessionCostUsd(agentRunId) {
1728
+ return agentRunId && agentRunId === AGENT_TELEMETRY_CONTEXT.agentRunId ? AGENT_TELEMETRY_CONTEXT.sessionCostUsd : void 0;
1729
+ }
1730
+ var sessionCostListeners = /* @__PURE__ */ new Set();
1731
+ function onSessionCost(listener) {
1732
+ sessionCostListeners.add(listener);
1733
+ return () => {
1734
+ sessionCostListeners.delete(listener);
1735
+ };
1736
+ }
1737
+ function sessionTags() {
1738
+ const { agentRunId } = AGENT_TELEMETRY_CONTEXT;
1739
+ return agentRunId ? [`agent_run_id:${agentRunId}`] : [];
1740
+ }
1741
+ var UNKNOWN_STACK = "unknown";
1742
+ function sessionSummary(outcome, session) {
1743
+ const completedSteps = session?.completedSteps ?? [];
1744
+ return {
1745
+ outcome,
1746
+ estimated_cost_usd: AGENT_TELEMETRY_CONTEXT.sessionCostUsd,
1747
+ completed_steps: truncate2(completedSteps.join(", ")),
1748
+ completed_step_count: completedSteps.length,
1749
+ language: truncate2(session?.language ?? UNKNOWN_STACK),
1750
+ framework: truncate2(session?.framework ?? UNKNOWN_STACK)
1751
+ };
1752
+ }
1708
1753
  function isTelemetryOptedOut() {
1709
1754
  return process.env.WIZARD_TELEMETRY === "false";
1710
1755
  }
@@ -1837,13 +1882,15 @@ function agentRunMetrics(event, values, tags) {
1837
1882
  function trackAgentRun(event) {
1838
1883
  const profile = getModelProfile(event.profile);
1839
1884
  const values = agentRunValues(event);
1885
+ AGENT_TELEMETRY_CONTEXT.sessionCostUsd += values.estimatedCostUsd;
1840
1886
  const tagsForMetrics = agentMetricTags(
1841
1887
  event,
1842
1888
  AGENT_TELEMETRY_CONTEXT.workflowId
1843
1889
  );
1844
1890
  const tags = [
1845
1891
  ...tagsForMetrics,
1846
- ...AGENT_TELEMETRY_CONTEXT.appId ? [`app_id:${AGENT_TELEMETRY_CONTEXT.appId}`] : []
1892
+ ...AGENT_TELEMETRY_CONTEXT.appId ? [`app_id:${AGENT_TELEMETRY_CONTEXT.appId}`] : [],
1893
+ ...sessionTags()
1847
1894
  ];
1848
1895
  sendTelemetry({
1849
1896
  logs: [
@@ -1864,6 +1911,7 @@ function trackAgentRun(event) {
1864
1911
  finishReason: event.finishReason,
1865
1912
  workflow_id: AGENT_TELEMETRY_CONTEXT.workflowId,
1866
1913
  app_id: AGENT_TELEMETRY_CONTEXT.appId,
1914
+ agent_run_id: AGENT_TELEMETRY_CONTEXT.agentRunId,
1867
1915
  ...values
1868
1916
  },
1869
1917
  tags
@@ -1871,14 +1919,26 @@ function trackAgentRun(event) {
1871
1919
  ],
1872
1920
  metrics: agentRunMetrics(event, values, tagsForMetrics)
1873
1921
  });
1922
+ for (const listener of sessionCostListeners) {
1923
+ try {
1924
+ listener();
1925
+ } catch (err) {
1926
+ logger.warn({ err }, "session cost listener failed");
1927
+ }
1928
+ }
1874
1929
  }
1875
1930
  function logTags(workflowId, actionId, appId) {
1876
1931
  return [
1877
1932
  ...metricTags(workflowId, actionId),
1878
- ...appId ? [`app_id:${appId}`] : []
1933
+ ...appId ? [`app_id:${appId}`] : [],
1934
+ ...sessionTags()
1879
1935
  ];
1880
1936
  }
1881
- function emitTelemetryLog(level, message, attributes, tags) {
1937
+ function emitTelemetryLog(level, message, fields, tags) {
1938
+ const attributes = {
1939
+ ...fields,
1940
+ agent_run_id: AGENT_TELEMETRY_CONTEXT.agentRunId
1941
+ };
1882
1942
  if (level === "info") {
1883
1943
  logger.info(attributes, message);
1884
1944
  } else {
@@ -1889,6 +1949,8 @@ function emitTelemetryLog(level, message, attributes, tags) {
1889
1949
  function trackWorkflowStart(ctx) {
1890
1950
  AGENT_TELEMETRY_CONTEXT.appId = ctx.appId;
1891
1951
  AGENT_TELEMETRY_CONTEXT.workflowId = ctx.workflowId;
1952
+ AGENT_TELEMETRY_CONTEXT.agentRunId = ctx.agentRunId;
1953
+ AGENT_TELEMETRY_CONTEXT.sessionCostUsd = ctx.estimatedCostUsd ?? NO_COST;
1892
1954
  const attributes = {
1893
1955
  event: "wizard.workflow.start",
1894
1956
  workflow_id: ctx.workflowId,
@@ -1907,7 +1969,8 @@ function trackWorkflowAbandoned(ctx) {
1907
1969
  event: "wizard.workflow.abandoned",
1908
1970
  workflow_id: ctx.workflowId,
1909
1971
  app_id: ctx.appId,
1910
- reason: ctx.reason
1972
+ reason: ctx.reason,
1973
+ ...sessionSummary("abandoned", ctx.session)
1911
1974
  };
1912
1975
  emitTelemetryLog(
1913
1976
  "info",
@@ -2037,7 +2100,8 @@ function trackWizardComplete(event) {
2037
2100
  workflow_id: event.workflowId,
2038
2101
  app_id: event.appId,
2039
2102
  total_duration: event.total_duration,
2040
- total_steps: event.total_steps
2103
+ total_steps: event.total_steps,
2104
+ ...sessionSummary("success", event)
2041
2105
  };
2042
2106
  emitTelemetryLog(
2043
2107
  "info",
@@ -2045,8 +2109,7 @@ function trackWizardComplete(event) {
2045
2109
  attributes,
2046
2110
  logTags(event.workflowId, void 0, event.appId)
2047
2111
  );
2048
- AGENT_TELEMETRY_CONTEXT.appId = void 0;
2049
- AGENT_TELEMETRY_CONTEXT.workflowId = void 0;
2112
+ resetTelemetryContext();
2050
2113
  }
2051
2114
  function trackWorkflowError(ctx) {
2052
2115
  const attributes = {
@@ -2055,7 +2118,8 @@ function trackWorkflowError(ctx) {
2055
2118
  action_id: ctx.actionId,
2056
2119
  error: ctx.error,
2057
2120
  app_id: ctx.appId,
2058
- ...errorAttributes(ctx.cause)
2121
+ ...errorAttributes(ctx.cause),
2122
+ ...sessionSummary("failed", ctx.session)
2059
2123
  };
2060
2124
  emitTelemetryLog(
2061
2125
  "error",
@@ -2063,8 +2127,7 @@ function trackWorkflowError(ctx) {
2063
2127
  attributes,
2064
2128
  logTags(ctx.workflowId, ctx.actionId, ctx.appId)
2065
2129
  );
2066
- AGENT_TELEMETRY_CONTEXT.appId = void 0;
2067
- AGENT_TELEMETRY_CONTEXT.workflowId = void 0;
2130
+ resetTelemetryContext();
2068
2131
  }
2069
2132
 
2070
2133
  // src/lib/events.ts
@@ -2384,7 +2447,7 @@ async function ensureApplication(resumeAppId) {
2384
2447
  // package.json
2385
2448
  var package_default = {
2386
2449
  name: "@algolia/wizard",
2387
- version: "0.98.0",
2450
+ version: "0.99.0",
2388
2451
  description: "Magically implement Algolia functionality in your codebase",
2389
2452
  type: "module",
2390
2453
  engines: {
@@ -2528,6 +2591,36 @@ function initWorkflowState(workflow, now, appId) {
2528
2591
  function ensureAgentRunId(state) {
2529
2592
  if (!state.agentRunId) state.agentRunId = crypto.randomUUID();
2530
2593
  }
2594
+ async function persistState(state) {
2595
+ state.estimatedCostUsd = sessionCostUsd(state.agentRunId) ?? state.estimatedCostUsd;
2596
+ await saveWorkflowState(state);
2597
+ }
2598
+ function saveCostDuringStep(state) {
2599
+ const snapshot = JSON.parse(JSON.stringify(state));
2600
+ return onSessionCost(() => {
2601
+ const cost = sessionCostUsd(state.agentRunId);
2602
+ if (cost === void 0) return;
2603
+ snapshot.estimatedCostUsd = cost;
2604
+ saveWorkflowState(snapshot).catch((err) => {
2605
+ logger.warn({ err }, "failed to save the session cost");
2606
+ });
2607
+ });
2608
+ }
2609
+ function stepOutput(state, stepId) {
2610
+ const record = state.steps.find((s) => s.id === stepId);
2611
+ return record?.status === "done" ? record.output : void 0;
2612
+ }
2613
+ function stackName(state, confirmStepId, key) {
2614
+ const name = stepOutput(state, confirmStepId)?.[key]?.[0]?.name ?? stepOutput(state, "project-scan")?.[key]?.[0]?.name;
2615
+ return name ? name.toLowerCase() : "unknown";
2616
+ }
2617
+ function sessionReport(state) {
2618
+ return {
2619
+ completedSteps: state.steps.filter((s) => s.status === "done").map((s) => s.id),
2620
+ language: stackName(state, "confirm-language", "languages"),
2621
+ framework: stackName(state, "confirm-framework", "frameworks")
2622
+ };
2623
+ }
2531
2624
  function completeEventProperties(state, totalDurationMs, stepCount) {
2532
2625
  const indexName = state.steps.find((s) => s.id === "select-index")?.output?.selection;
2533
2626
  const framework = state.steps.find((s) => s.id === "confirm-framework")?.output?.frameworks?.[0]?.name;
@@ -2632,7 +2725,7 @@ async function rewindTo(state, targetIndex, fromIndex) {
2632
2725
  const store = useWizard.getState();
2633
2726
  store.setActiveStep(targetIndex);
2634
2727
  store.syncSteps([...state.steps], targetIndex);
2635
- await saveWorkflowState(state);
2728
+ await persistState(state);
2636
2729
  }
2637
2730
  async function runStep(state, index, step) {
2638
2731
  const store = useWizard.getState();
@@ -2653,9 +2746,10 @@ async function runStep(state, index, step) {
2653
2746
  state.updatedAt = nowIso();
2654
2747
  store.setActiveStep(index);
2655
2748
  store.syncSteps([...state.steps], index);
2656
- await saveWorkflowState(state);
2749
+ await persistState(state);
2657
2750
  await markInteraction();
2658
2751
  const ctx = await makeContext(state);
2752
+ const stopSavingCost = saveCostDuringStep(state);
2659
2753
  let output;
2660
2754
  try {
2661
2755
  const raw = await step.run(ctx);
@@ -2663,6 +2757,7 @@ async function runStep(state, index, step) {
2663
2757
  await step.onStepComplete?.(output, ctx);
2664
2758
  } catch (err) {
2665
2759
  if (err instanceof ReturnToStepSignal) {
2760
+ stopSavingCost();
2666
2761
  await rewindTo(state, err.targetIndex, index);
2667
2762
  return err.targetIndex;
2668
2763
  }
@@ -2677,12 +2772,14 @@ async function runStep(state, index, step) {
2677
2772
  });
2678
2773
  }
2679
2774
  throw err;
2775
+ } finally {
2776
+ stopSavingCost();
2680
2777
  }
2681
2778
  record.status = "done";
2682
2779
  record.output = output;
2683
2780
  state.updatedAt = nowIso();
2684
2781
  store.syncSteps([...state.steps], index);
2685
- await saveWorkflowState(state);
2782
+ await persistState(state);
2686
2783
  trackActionEnd({
2687
2784
  workflowId: state.workflowId,
2688
2785
  appId,
@@ -2740,13 +2837,19 @@ async function beginRun(workflow, state) {
2740
2837
  [...state.steps]
2741
2838
  );
2742
2839
  await ensureConsent();
2743
- trackWorkflowStart({ workflowId: workflow.id, appId: runAppId(state) });
2840
+ trackWorkflowStart({
2841
+ workflowId: workflow.id,
2842
+ appId: runAppId(state),
2843
+ agentRunId: state.agentRunId,
2844
+ estimatedCostUsd: state.estimatedCostUsd
2845
+ });
2744
2846
  }
2745
2847
  async function handleRestartWorkflow(workflow, previous, newAppId) {
2746
2848
  trackWorkflowAbandoned({
2747
2849
  workflowId: workflow.id,
2748
2850
  appId: runAppId(previous),
2749
- reason: "application_changed"
2851
+ reason: "application_changed",
2852
+ session: sessionReport(previous)
2750
2853
  });
2751
2854
  await clearWorkflowState(workflow.id);
2752
2855
  const next = initWorkflowState(workflow, nowIso(), newAppId);
@@ -2795,7 +2898,8 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
2795
2898
  workflowId: workflow.id,
2796
2899
  appId: runAppId(state),
2797
2900
  total_duration: Math.round(totalDurationMs / 1e3),
2798
- total_steps: stepCount
2901
+ total_steps: stepCount,
2902
+ ...sessionReport(state)
2799
2903
  });
2800
2904
  track(
2801
2905
  "AI Wizard Completed",
@@ -2819,12 +2923,12 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
2819
2923
  if (record.status === "running") {
2820
2924
  record.status = "error";
2821
2925
  record.error = message;
2822
- await saveWorkflowState(failed);
2823
2926
  store.syncSteps([...failed.steps], failed.currentStepIndex);
2824
2927
  }
2825
2928
  failedActionId = record.id;
2826
2929
  failedActionTitle = record.title;
2827
2930
  }
2931
+ await persistState(failed);
2828
2932
  }
2829
2933
  if (failedActionId && failedActionTitle) {
2830
2934
  trackActionError({
@@ -2841,7 +2945,8 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
2841
2945
  appId: failedAppId,
2842
2946
  error: message,
2843
2947
  actionId: failedActionId,
2844
- cause: err
2948
+ cause: err,
2949
+ session: failed ? sessionReport(failed) : void 0
2845
2950
  });
2846
2951
  const errorType = wizardErrorType(failedActionId, step, message);
2847
2952
  const errorCode2 = errorCodeFrom(err);
@@ -9911,7 +10016,7 @@ async function resetProjectState() {
9911
10016
  // package.json with { type: 'json' }
9912
10017
  var package_default2 = {
9913
10018
  name: "@algolia/wizard",
9914
- version: "0.98.0",
10019
+ version: "0.99.0",
9915
10020
  description: "Magically implement Algolia functionality in your codebase",
9916
10021
  type: "module",
9917
10022
  engines: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.98.0",
3
+ "version": "0.99.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {