@runhuman/sensor 0.3.0 → 0.3.1

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.
package/dist/index.d.mts CHANGED
@@ -52,6 +52,7 @@ interface SessionManagerConfig {
52
52
  }
53
53
  declare class SessionManager {
54
54
  private state;
55
+ private _disabled;
55
56
  private sessionId;
56
57
  private activeJobId;
57
58
  private pollTimer;
@@ -66,6 +67,8 @@ declare class SessionManager {
66
67
  getSnapshot(): SessionState;
67
68
  getSessionId(): string | null;
68
69
  getActiveJobId(): string | null;
70
+ /** True when the API rejected the session (e.g. subscription tier). Hides the overlay. */
71
+ isDisabled(): boolean;
69
72
  /**
70
73
  * Subscribe to state changes. Returns an unsubscribe function.
71
74
  * Compatible with React's useSyncExternalStore.
@@ -180,6 +183,8 @@ interface RunhumanProviderProps {
180
183
  children: React.ReactNode;
181
184
  /** API key for authentication (e.g., 'rh_...') */
182
185
  apiKey: string;
186
+ /** Set to false to disable the sensor entirely (e.g., in production). Default: true */
187
+ enabled?: boolean;
183
188
  /** Corner for the overlay UI (default: 'bottom-right') */
184
189
  position?: OverlayPosition;
185
190
  /** Base URL of the Runhuman API (defaults to production) */
@@ -199,7 +204,7 @@ interface RunhumanProviderProps {
199
204
  /** Log debug info to console (default: false) */
200
205
  debug?: boolean;
201
206
  }
202
- declare function RunhumanProvider({ children, apiKey, position, baseUrl, jobId, platform, flushIntervalMs, pollIntervalMs, maxBufferSize, enableDeepLinks, debug, }: RunhumanProviderProps): react_jsx_runtime.JSX.Element;
207
+ declare function RunhumanProvider({ children, apiKey, enabled, position, baseUrl, jobId, platform, flushIntervalMs, pollIntervalMs, maxBufferSize, enableDeepLinks, debug, }: RunhumanProviderProps): react_jsx_runtime.JSX.Element;
203
208
 
204
209
  /**
205
210
  * React hook for subscribing to sensor state changes.
@@ -210,6 +215,7 @@ interface SensorState {
210
215
  state: SessionState;
211
216
  activeJobId: string | null;
212
217
  sessionId: string | null;
218
+ disabled: boolean;
213
219
  }
214
220
  /**
215
221
  * Subscribe to the sensor's session state.
package/dist/index.d.ts CHANGED
@@ -52,6 +52,7 @@ interface SessionManagerConfig {
52
52
  }
53
53
  declare class SessionManager {
54
54
  private state;
55
+ private _disabled;
55
56
  private sessionId;
56
57
  private activeJobId;
57
58
  private pollTimer;
@@ -66,6 +67,8 @@ declare class SessionManager {
66
67
  getSnapshot(): SessionState;
67
68
  getSessionId(): string | null;
68
69
  getActiveJobId(): string | null;
70
+ /** True when the API rejected the session (e.g. subscription tier). Hides the overlay. */
71
+ isDisabled(): boolean;
69
72
  /**
70
73
  * Subscribe to state changes. Returns an unsubscribe function.
71
74
  * Compatible with React's useSyncExternalStore.
@@ -180,6 +183,8 @@ interface RunhumanProviderProps {
180
183
  children: React.ReactNode;
181
184
  /** API key for authentication (e.g., 'rh_...') */
182
185
  apiKey: string;
186
+ /** Set to false to disable the sensor entirely (e.g., in production). Default: true */
187
+ enabled?: boolean;
183
188
  /** Corner for the overlay UI (default: 'bottom-right') */
184
189
  position?: OverlayPosition;
185
190
  /** Base URL of the Runhuman API (defaults to production) */
@@ -199,7 +204,7 @@ interface RunhumanProviderProps {
199
204
  /** Log debug info to console (default: false) */
200
205
  debug?: boolean;
201
206
  }
202
- declare function RunhumanProvider({ children, apiKey, position, baseUrl, jobId, platform, flushIntervalMs, pollIntervalMs, maxBufferSize, enableDeepLinks, debug, }: RunhumanProviderProps): react_jsx_runtime.JSX.Element;
207
+ declare function RunhumanProvider({ children, apiKey, enabled, position, baseUrl, jobId, platform, flushIntervalMs, pollIntervalMs, maxBufferSize, enableDeepLinks, debug, }: RunhumanProviderProps): react_jsx_runtime.JSX.Element;
203
208
 
204
209
  /**
205
210
  * React hook for subscribing to sensor state changes.
@@ -210,6 +215,7 @@ interface SensorState {
210
215
  state: SessionState;
211
216
  activeJobId: string | null;
212
217
  sessionId: string | null;
218
+ disabled: boolean;
213
219
  }
214
220
  /**
215
221
  * Subscribe to the sensor's session state.
package/dist/index.js CHANGED
@@ -197,6 +197,8 @@ var apiRoutes = {
197
197
  job: defineRoute("/jobs/:jobId"),
198
198
  /** Cancel a job */
199
199
  jobCancel: defineRoute("/jobs/:jobId/cancel"),
200
+ /** Create a GitHub issue from an extracted finding (user-initiated) */
201
+ jobCreateIssue: defineRoute("/jobs/:jobId/create-issue"),
200
202
  /** Get job status (for polling) */
201
203
  jobStatus: defineRoute("/jobs/:jobId/status"),
202
204
  /** Get individual job artifact by type */
@@ -858,6 +860,7 @@ var InterceptorManager = class {
858
860
  var SessionManager = class {
859
861
  constructor(config) {
860
862
  this.state = "idle";
863
+ this._disabled = false;
861
864
  this.sessionId = null;
862
865
  this.activeJobId = null;
863
866
  this.pollTimer = null;
@@ -880,6 +883,10 @@ var SessionManager = class {
880
883
  getActiveJobId() {
881
884
  return this.activeJobId;
882
885
  }
886
+ /** True when the API rejected the session (e.g. subscription tier). Hides the overlay. */
887
+ isDisabled() {
888
+ return this._disabled;
889
+ }
883
890
  /**
884
891
  * Subscribe to state changes. Returns an unsubscribe function.
885
892
  * Compatible with React's useSyncExternalStore.
@@ -964,12 +971,22 @@ var SessionManager = class {
964
971
  async startSession() {
965
972
  this.setState("active");
966
973
  const now = /* @__PURE__ */ new Date();
967
- const response = await this.config.apiClient.createSession({
968
- jobId: this.activeJobId,
969
- platform: this.config.platform,
970
- sdkVersion: this.config.sdkVersion,
971
- clientStartTime: now.toISOString()
972
- });
974
+ let response;
975
+ try {
976
+ response = await this.config.apiClient.createSession({
977
+ jobId: this.activeJobId,
978
+ platform: this.config.platform,
979
+ sdkVersion: this.config.sdkVersion,
980
+ clientStartTime: now.toISOString()
981
+ });
982
+ } catch (error) {
983
+ const is403 = error instanceof Error && error.message.includes("403");
984
+ this.log(is403 ? "Subscription not eligible \u2014 disabling sensor" : "Session creation failed", { error });
985
+ if (is403) this._disabled = true;
986
+ this.activeJobId = null;
987
+ this.setState("idle");
988
+ return;
989
+ }
973
990
  this.sessionId = response.sessionId;
974
991
  const sessionStartTime = now.getTime();
975
992
  this.log("Session started", {
@@ -1199,7 +1216,7 @@ var import_react_native4 = require("react-native");
1199
1216
  // src/overlay/use-sensor-state.ts
1200
1217
  var import_react = require("react");
1201
1218
  var import_react2 = require("react");
1202
- var IDLE_STATE = { state: "idle", activeJobId: null, sessionId: null };
1219
+ var IDLE_STATE = { state: "idle", activeJobId: null, sessionId: null, disabled: false };
1203
1220
  function tryGetInstance() {
1204
1221
  try {
1205
1222
  return Runhuman.getInstance();
@@ -1235,11 +1252,12 @@ function useSensorState() {
1235
1252
  const state = sm.getSnapshot();
1236
1253
  const activeJobId = sm.getActiveJobId();
1237
1254
  const sessionId = sm.getSessionId();
1255
+ const disabled = sm.isDisabled();
1238
1256
  const prev = cached.current;
1239
- if (prev.state === state && prev.activeJobId === activeJobId && prev.sessionId === sessionId) {
1257
+ if (prev.state === state && prev.activeJobId === activeJobId && prev.sessionId === sessionId && prev.disabled === disabled) {
1240
1258
  return prev;
1241
1259
  }
1242
- cached.current = { state, activeJobId, sessionId };
1260
+ cached.current = { state, activeJobId, sessionId, disabled };
1243
1261
  return cached.current;
1244
1262
  }, []);
1245
1263
  return (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
@@ -1403,12 +1421,18 @@ function CodeEntryPanel({ position }) {
1403
1421
  if (!trimmed) return;
1404
1422
  setResolving(true);
1405
1423
  setError(null);
1406
- const instance = Runhuman.getInstance();
1407
- const result = await instance.getApiClient().resolveShortCode(trimmed);
1408
- if (result) {
1409
- instance.activate(result.jobId);
1410
- } else {
1411
- setError("Invalid or expired code");
1424
+ try {
1425
+ const instance = Runhuman.getInstance();
1426
+ const result = await instance.getApiClient().resolveShortCode(trimmed);
1427
+ if (result) {
1428
+ await instance.activate(result.jobId);
1429
+ } else {
1430
+ setError("Invalid or expired code");
1431
+ setResolving(false);
1432
+ }
1433
+ } catch (err) {
1434
+ const message = err instanceof Error ? err.message : "Activation failed";
1435
+ setError(message);
1412
1436
  setResolving(false);
1413
1437
  }
1414
1438
  };
@@ -1499,6 +1523,7 @@ var positionMap = {
1499
1523
  function RunhumanProvider({
1500
1524
  children,
1501
1525
  apiKey,
1526
+ enabled = true,
1502
1527
  position = "bottom-right",
1503
1528
  baseUrl,
1504
1529
  jobId,
@@ -1511,7 +1536,7 @@ function RunhumanProvider({
1511
1536
  }) {
1512
1537
  const initializedRef = (0, import_react5.useRef)(false);
1513
1538
  (0, import_react5.useEffect)(() => {
1514
- if (initializedRef.current) return;
1539
+ if (!enabled || initializedRef.current) return;
1515
1540
  initializedRef.current = true;
1516
1541
  const config = {
1517
1542
  apiKey,
@@ -1529,13 +1554,14 @@ function RunhumanProvider({
1529
1554
  initializedRef.current = false;
1530
1555
  Runhuman.getInstance().destroy();
1531
1556
  };
1532
- }, []);
1533
- const { state, activeJobId } = useSensorState();
1557
+ }, [enabled]);
1558
+ const { state, activeJobId, disabled } = useSensorState();
1559
+ const showOverlay = enabled && !disabled;
1534
1560
  const posKey = positionMap[position];
1535
1561
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles.container, children: [
1536
1562
  children,
1537
- state === "idle" && !activeJobId ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CodeEntryPanel, { position: posKey }) : null,
1538
- state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActiveIndicator, { state, position: posKey }) : null
1563
+ showOverlay && state === "idle" && !activeJobId ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CodeEntryPanel, { position: posKey }) : null,
1564
+ showOverlay && state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActiveIndicator, { state, position: posKey }) : null
1539
1565
  ] });
1540
1566
  }
1541
1567
  var styles = import_react_native4.StyleSheet.create({