@runhuman/sensor 0.1.3 → 0.2.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
@@ -1,5 +1,7 @@
1
1
  import { TelemetryPlatform } from '@runhuman/shared';
2
2
  export { TelemetryPlatform } from '@runhuman/shared';
3
+ import { S as SessionManager, A as ApiClient } from './session-manager-B6tiwEQm.mjs';
4
+ export { a as SessionState } from './session-manager-B6tiwEQm.mjs';
3
5
 
4
6
  /**
5
7
  * Runhuman Sensor — Public API
@@ -35,6 +37,7 @@ interface RunhumanConfig {
35
37
  }
36
38
  declare class Runhuman {
37
39
  private static instance;
40
+ private readonly apiClient;
38
41
  private readonly sessionManager;
39
42
  private readonly deepLinkHandler;
40
43
  private readonly debug;
@@ -68,7 +71,11 @@ declare class Runhuman {
68
71
  * After calling destroy(), you can call init() again.
69
72
  */
70
73
  destroy(): Promise<void>;
74
+ /** Access the session manager (used by <RunhumanOverlay /> for reactive state) */
75
+ getSessionManager(): SessionManager;
76
+ /** Access the API client (used by <RunhumanOverlay /> for short code resolution) */
77
+ getApiClient(): ApiClient;
71
78
  private log;
72
79
  }
73
80
 
74
- export { Runhuman, type RunhumanConfig };
81
+ export { ApiClient, Runhuman, type RunhumanConfig };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { TelemetryPlatform } from '@runhuman/shared';
2
2
  export { TelemetryPlatform } from '@runhuman/shared';
3
+ import { S as SessionManager, A as ApiClient } from './session-manager-B6tiwEQm.js';
4
+ export { a as SessionState } from './session-manager-B6tiwEQm.js';
3
5
 
4
6
  /**
5
7
  * Runhuman Sensor — Public API
@@ -35,6 +37,7 @@ interface RunhumanConfig {
35
37
  }
36
38
  declare class Runhuman {
37
39
  private static instance;
40
+ private readonly apiClient;
38
41
  private readonly sessionManager;
39
42
  private readonly deepLinkHandler;
40
43
  private readonly debug;
@@ -68,7 +71,11 @@ declare class Runhuman {
68
71
  * After calling destroy(), you can call init() again.
69
72
  */
70
73
  destroy(): Promise<void>;
74
+ /** Access the session manager (used by <RunhumanOverlay /> for reactive state) */
75
+ getSessionManager(): SessionManager;
76
+ /** Access the API client (used by <RunhumanOverlay /> for short code resolution) */
77
+ getApiClient(): ApiClient;
71
78
  private log;
72
79
  }
73
80
 
74
- export { Runhuman, type RunhumanConfig };
81
+ export { ApiClient, Runhuman, type RunhumanConfig };
package/dist/index.js CHANGED
@@ -93,6 +93,8 @@ var webRoutes = {
93
93
  organizationGitHub: defineRoute("/dashboard/organizations/:organizationId/github"),
94
94
  /** Organization SSO/SAML configuration page */
95
95
  organizationSso: defineRoute("/dashboard/organizations/:organizationId/sso"),
96
+ /** Organization schedules overview */
97
+ organizationSchedules: defineRoute("/dashboard/organizations/:organizationId/schedules"),
96
98
  /** Import from GitHub page */
97
99
  organizationImportGitHub: defineRoute("/dashboard/organizations/:organizationId/import"),
98
100
  // ============================================
@@ -138,6 +140,8 @@ var webRoutes = {
138
140
  issueSession: defineRoute("/dashboard/:projectId/issue-sessions/:sessionId"),
139
141
  /** Project settings page */
140
142
  projectSettings: defineRoute("/dashboard/:projectId/settings"),
143
+ /** Schedules page */
144
+ schedules: defineRoute("/dashboard/:projectId/schedules"),
141
145
  /** Flow charts page */
142
146
  flowCharts: defineRoute("/dashboard/:projectId/flowcharts"),
143
147
  /** Single flow chart detail (embedded in dashboard - deprecated, use flowChartView) */
@@ -181,6 +185,8 @@ var apiRoutes = {
181
185
  job: defineRoute("/jobs/:jobId"),
182
186
  /** Get job status (for polling) */
183
187
  jobStatus: defineRoute("/jobs/:jobId/status"),
188
+ /** Get individual job artifact by type */
189
+ jobArtifact: defineRoute("/jobs/:jobId/artifacts/:artifactType"),
184
190
  /** Update job feedback and rating */
185
191
  jobFeedback: defineRoute("/jobs/:jobId/feedback"),
186
192
  /** Enable/disable public sharing for a job (POST = enable, DELETE = disable) */
@@ -276,6 +282,12 @@ var apiRoutes = {
276
282
  projectFlowChartChat: defineRoute("/projects/:projectId/flowcharts/:flowchartId/chat"),
277
283
  /** Enable/disable public sharing for a flow chart */
278
284
  projectFlowChartShare: defineRoute("/projects/:projectId/flowcharts/:flowchartId/share"),
285
+ /** List schedules for a project (GET) or create schedule (POST) */
286
+ projectSchedules: defineRoute("/projects/:projectId/schedules"),
287
+ /** Get/update/delete specific schedule */
288
+ projectSchedule: defineRoute("/projects/:projectId/schedules/:scheduleId"),
289
+ /** List execution history for a schedule */
290
+ projectScheduleExecutions: defineRoute("/projects/:projectId/schedules/:scheduleId/executions"),
279
291
  /** Bulk create projects from GitHub repos */
280
292
  bulkCreateProjects: defineRoute("/projects/bulk"),
281
293
  // ============================================
@@ -457,6 +469,8 @@ var apiRoutes = {
457
469
  organizationGitHubRepoCheckAccess: defineRoute("/organizations/:organizationId/github/repos/check-access"),
458
470
  /** Find deployed URL for a repo via org's GitHub installations */
459
471
  organizationGitHubRepoFindUrl: defineRoute("/organizations/:organizationId/github/repos/find-url"),
472
+ /** Get all schedules across projects in the organization */
473
+ organizationSchedules: defineRoute("/organizations/:organizationId/schedules"),
460
474
  /** Get organization billing balance (Polar) */
461
475
  organizationBilling: defineRoute("/organizations/:organizationId/billing"),
462
476
  // ============================================
@@ -517,7 +531,9 @@ var apiRoutes = {
517
531
  /** Submit telemetry event batch (POST) — SDK periodically flushes captured events */
518
532
  telemetryBatch: defineRoute("/telemetry/sessions/:sessionId/events"),
519
533
  /** Check session status for a job (GET) — SDK polls to know when to activate */
520
- telemetrySessionStatus: defineRoute("/telemetry/jobs/:jobId/status")
534
+ telemetrySessionStatus: defineRoute("/telemetry/jobs/:jobId/status"),
535
+ /** Resolve a sensor short code to a jobId (GET) — SDK calls this when tester enters a code */
536
+ telemetryShortCodeResolve: defineRoute("/telemetry/short-codes/:code")
521
537
  };
522
538
 
523
539
  // src/api-client.ts
@@ -548,6 +564,17 @@ var ApiClient = class {
548
564
  async getSessionStatus(jobId) {
549
565
  return this.get(apiRoutes.telemetrySessionStatus.build({ jobId }));
550
566
  }
567
+ async resolveShortCode(code) {
568
+ const normalized = code.replace(/^RH-/i, "").toUpperCase();
569
+ try {
570
+ return await this.get(
571
+ apiRoutes.telemetryShortCodeResolve.build({ code: normalized })
572
+ );
573
+ } catch (error) {
574
+ if (error instanceof Error && error.message.includes("404")) return null;
575
+ throw error;
576
+ }
577
+ }
551
578
  url(routePath) {
552
579
  return `${this.baseUrl}${API_PREFIX}${routePath}`;
553
580
  }
@@ -817,6 +844,7 @@ var SessionManager = class {
817
844
  this.activeJobId = null;
818
845
  this.pollTimer = null;
819
846
  this.flushTimer = null;
847
+ this.listeners = /* @__PURE__ */ new Set();
820
848
  this.config = config;
821
849
  this.buffer = new EventBuffer(config.maxBufferSize);
822
850
  this.interceptors = new InterceptorManager();
@@ -824,9 +852,30 @@ var SessionManager = class {
824
852
  getState() {
825
853
  return this.state;
826
854
  }
855
+ /** Alias for getState() — named for React's useSyncExternalStore convention */
856
+ getSnapshot() {
857
+ return this.state;
858
+ }
827
859
  getSessionId() {
828
860
  return this.sessionId;
829
861
  }
862
+ getActiveJobId() {
863
+ return this.activeJobId;
864
+ }
865
+ /**
866
+ * Subscribe to state changes. Returns an unsubscribe function.
867
+ * Compatible with React's useSyncExternalStore.
868
+ */
869
+ subscribe(listener) {
870
+ this.listeners.add(listener);
871
+ return () => this.listeners.delete(listener);
872
+ }
873
+ setState(newState) {
874
+ this.state = newState;
875
+ for (const listener of this.listeners) {
876
+ listener();
877
+ }
878
+ }
830
879
  /**
831
880
  * Start polling for job activation.
832
881
  * The sensor calls this on init — polling continues until a job is detected
@@ -835,7 +884,7 @@ var SessionManager = class {
835
884
  startPolling(jobId) {
836
885
  if (this.state !== "idle") return;
837
886
  this.activeJobId = jobId;
838
- this.state = "polling";
887
+ this.setState("polling");
839
888
  this.log("Polling started", { jobId });
840
889
  this.pollTimer = setInterval(() => this.pollOnce(), this.config.pollIntervalMs);
841
890
  this.pollOnce();
@@ -846,7 +895,7 @@ var SessionManager = class {
846
895
  this.pollTimer = null;
847
896
  }
848
897
  if (this.state === "polling") {
849
- this.state = "idle";
898
+ this.setState("idle");
850
899
  this.log("Polling stopped");
851
900
  }
852
901
  }
@@ -883,10 +932,9 @@ var SessionManager = class {
883
932
  if (this.state !== "polling" || !this.activeJobId) return;
884
933
  try {
885
934
  const status = await this.config.apiClient.getSessionStatus(this.activeJobId);
886
- if (status.jobActive && status.sdkEnabled && status.activeSessionId) {
887
- this.log("Job has active session, starting capture", {
888
- jobId: this.activeJobId,
889
- activeSessionId: status.activeSessionId
935
+ if (status.jobActive && !status.activeSessionId) {
936
+ this.log("Job is active, creating session", {
937
+ jobId: this.activeJobId
890
938
  });
891
939
  this.stopPolling();
892
940
  await this.startSession();
@@ -896,7 +944,7 @@ var SessionManager = class {
896
944
  }
897
945
  }
898
946
  async startSession() {
899
- this.state = "active";
947
+ this.setState("active");
900
948
  const now = /* @__PURE__ */ new Date();
901
949
  const response = await this.config.apiClient.createSession({
902
950
  jobId: this.activeJobId,
@@ -915,7 +963,7 @@ var SessionManager = class {
915
963
  this.flushTimer = setInterval(() => this.flush(), this.config.flushIntervalMs);
916
964
  }
917
965
  async endSession() {
918
- this.state = "ending";
966
+ this.setState("ending");
919
967
  if (this.flushTimer) {
920
968
  clearInterval(this.flushTimer);
921
969
  this.flushTimer = null;
@@ -937,7 +985,7 @@ var SessionManager = class {
937
985
  this.sessionId = null;
938
986
  this.activeJobId = null;
939
987
  this.buffer.clear();
940
- this.state = "idle";
988
+ this.setState("idle");
941
989
  }
942
990
  async flush() {
943
991
  if (!this.sessionId) return;
@@ -1028,12 +1076,12 @@ var SDK_VERSION = "0.1.0";
1028
1076
  var _Runhuman = class _Runhuman {
1029
1077
  constructor(config) {
1030
1078
  this.debug = config.debug === true;
1031
- const apiClient = new ApiClient(
1079
+ this.apiClient = new ApiClient(
1032
1080
  config.baseUrl ?? PRODUCTION_BASE_URL,
1033
1081
  config.apiKey
1034
1082
  );
1035
1083
  this.sessionManager = new SessionManager({
1036
- apiClient,
1084
+ apiClient: this.apiClient,
1037
1085
  platform: config.platform ?? "react-native",
1038
1086
  sdkVersion: SDK_VERSION,
1039
1087
  flushIntervalMs: config.flushIntervalMs ?? 5e3,
@@ -1104,6 +1152,14 @@ var _Runhuman = class _Runhuman {
1104
1152
  _Runhuman.instance = null;
1105
1153
  this.log("Destroyed");
1106
1154
  }
1155
+ /** Access the session manager (used by <RunhumanOverlay /> for reactive state) */
1156
+ getSessionManager() {
1157
+ return this.sessionManager;
1158
+ }
1159
+ /** Access the API client (used by <RunhumanOverlay /> for short code resolution) */
1160
+ getApiClient() {
1161
+ return this.apiClient;
1162
+ }
1107
1163
  log(message) {
1108
1164
  if (this.debug) {
1109
1165
  console.debug(`[Runhuman] ${message}`);