@runhuman/sensor 0.2.2 → 0.2.3
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 +129 -4
- package/dist/index.d.ts +129 -4
- package/dist/index.js +273 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +270 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -7
- package/dist/overlay/index.d.mts +0 -30
- package/dist/overlay/index.d.ts +0 -30
- package/dist/overlay/index.js +0 -1431
- package/dist/overlay/index.js.map +0 -1
- package/dist/overlay/index.mjs +0 -1410
- package/dist/overlay/index.mjs.map +0 -1
- package/dist/session-manager-B6tiwEQm.d.mts +0 -99
- package/dist/session-manager-B6tiwEQm.d.ts +0 -99
package/dist/overlay/index.js
DELETED
|
@@ -1,1431 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/overlay/index.ts
|
|
21
|
-
var overlay_exports = {};
|
|
22
|
-
__export(overlay_exports, {
|
|
23
|
-
RunhumanOverlay: () => RunhumanOverlay,
|
|
24
|
-
useSensorState: () => useSensorState
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(overlay_exports);
|
|
27
|
-
|
|
28
|
-
// src/overlay/RunhumanOverlay.tsx
|
|
29
|
-
var import_react_native4 = require("react-native");
|
|
30
|
-
|
|
31
|
-
// src/overlay/use-sensor-state.ts
|
|
32
|
-
var import_react = require("react");
|
|
33
|
-
|
|
34
|
-
// ../shared/dist/routes/route-builder.js
|
|
35
|
-
var API_PREFIX = "/api";
|
|
36
|
-
function defineRoute(pattern) {
|
|
37
|
-
const build = (params) => {
|
|
38
|
-
if (!params)
|
|
39
|
-
return pattern;
|
|
40
|
-
let result = pattern;
|
|
41
|
-
for (const [key, value] of Object.entries(params)) {
|
|
42
|
-
if (value !== void 0) {
|
|
43
|
-
result = result.replace(`:${key}?`, value);
|
|
44
|
-
result = result.replace(`:${key}`, value);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
result = result.replace(/\/:[^/]+\?/g, "");
|
|
48
|
-
result = result.replace(/\/+/g, "/");
|
|
49
|
-
if (result.length > 1 && result.endsWith("/")) {
|
|
50
|
-
result = result.slice(0, -1);
|
|
51
|
-
}
|
|
52
|
-
return result;
|
|
53
|
-
};
|
|
54
|
-
return {
|
|
55
|
-
pattern,
|
|
56
|
-
build
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ../shared/dist/routes/web-routes.js
|
|
61
|
-
var webRoutes = {
|
|
62
|
-
// ============================================
|
|
63
|
-
// Non-project routes (no projectId required)
|
|
64
|
-
// ============================================
|
|
65
|
-
/** Dashboard home page */
|
|
66
|
-
dashboard: defineRoute("/dashboard"),
|
|
67
|
-
/** Subscription management page */
|
|
68
|
-
managePlan: defineRoute("/dashboard/manage-plan"),
|
|
69
|
-
/** Plan selection / upgrade page */
|
|
70
|
-
plans: defineRoute("/dashboard/plans"),
|
|
71
|
-
/** Simple job view (redirects to full URL with projectId) */
|
|
72
|
-
jobSimple: defineRoute("/dashboard/jobs/:jobId"),
|
|
73
|
-
// ============================================
|
|
74
|
-
// Admin routes (@volter.ai only)
|
|
75
|
-
// ============================================
|
|
76
|
-
/** Admin panel */
|
|
77
|
-
admin: defineRoute("/dashboard/admin"),
|
|
78
|
-
// ============================================
|
|
79
|
-
// Organizations routes
|
|
80
|
-
// ============================================
|
|
81
|
-
/** Organizations list page */
|
|
82
|
-
organizations: defineRoute("/dashboard/organizations"),
|
|
83
|
-
/** Organization dashboard */
|
|
84
|
-
organization: defineRoute("/dashboard/organizations/:organizationId"),
|
|
85
|
-
/** Organization members page */
|
|
86
|
-
organizationMembers: defineRoute("/dashboard/organizations/:organizationId/members"),
|
|
87
|
-
/** Organization settings page */
|
|
88
|
-
organizationSettings: defineRoute("/dashboard/organizations/:organizationId/settings"),
|
|
89
|
-
/** Organization usage/billing page */
|
|
90
|
-
organizationUsage: defineRoute("/dashboard/organizations/:organizationId/usage"),
|
|
91
|
-
/** Organization projects page */
|
|
92
|
-
organizationProjects: defineRoute("/dashboard/organizations/:organizationId/projects"),
|
|
93
|
-
/** Organization jobs page (jobs across all projects) */
|
|
94
|
-
organizationJobs: defineRoute("/dashboard/organizations/:organizationId/jobs"),
|
|
95
|
-
/** Organization API keys page */
|
|
96
|
-
organizationApiKeys: defineRoute("/dashboard/organizations/:organizationId/api-keys"),
|
|
97
|
-
/** Organization integrations page */
|
|
98
|
-
organizationIntegrations: defineRoute("/dashboard/organizations/:organizationId/integrations"),
|
|
99
|
-
/** Organization GitHub integrations page */
|
|
100
|
-
organizationGitHub: defineRoute("/dashboard/organizations/:organizationId/github"),
|
|
101
|
-
/** Organization SSO/SAML configuration page */
|
|
102
|
-
organizationSso: defineRoute("/dashboard/organizations/:organizationId/sso"),
|
|
103
|
-
/** Organization schedules overview */
|
|
104
|
-
organizationSchedules: defineRoute("/dashboard/organizations/:organizationId/schedules"),
|
|
105
|
-
/** Import from GitHub page */
|
|
106
|
-
organizationImportGitHub: defineRoute("/dashboard/organizations/:organizationId/import"),
|
|
107
|
-
// ============================================
|
|
108
|
-
// User settings routes
|
|
109
|
-
// ============================================
|
|
110
|
-
/** Settings root (redirects to account) */
|
|
111
|
-
settings: defineRoute("/dashboard/settings"),
|
|
112
|
-
/** Account settings page */
|
|
113
|
-
settingsAccount: defineRoute("/dashboard/settings/account"),
|
|
114
|
-
// ============================================
|
|
115
|
-
// Tester portal routes
|
|
116
|
-
// ============================================
|
|
117
|
-
/** Tester dashboard/portal home */
|
|
118
|
-
tester: defineRoute("/tester"),
|
|
119
|
-
/** Tester available jobs list */
|
|
120
|
-
testerJobs: defineRoute("/tester/jobs"),
|
|
121
|
-
/** Tester settings page */
|
|
122
|
-
testerSettings: defineRoute("/tester/settings"),
|
|
123
|
-
/** Tester active test session */
|
|
124
|
-
testerTest: defineRoute("/tester/test/:jobId"),
|
|
125
|
-
// ============================================
|
|
126
|
-
// Project routes (require projectId)
|
|
127
|
-
// ============================================
|
|
128
|
-
/** Project root (redirects to playground) */
|
|
129
|
-
project: defineRoute("/dashboard/:projectId"),
|
|
130
|
-
/** Playground page for testing */
|
|
131
|
-
playground: defineRoute("/dashboard/:projectId/playground"),
|
|
132
|
-
/** Jobs list page */
|
|
133
|
-
jobs: defineRoute("/dashboard/:projectId/jobs"),
|
|
134
|
-
/** Single job detail page */
|
|
135
|
-
job: defineRoute("/dashboard/:projectId/jobs/:jobId"),
|
|
136
|
-
/** Templates page */
|
|
137
|
-
templates: defineRoute("/dashboard/:projectId/templates"),
|
|
138
|
-
/** Single template detail (modal) */
|
|
139
|
-
template: defineRoute("/dashboard/:projectId/templates/:templateId"),
|
|
140
|
-
/** GitHub issues page */
|
|
141
|
-
issues: defineRoute("/dashboard/:projectId/issues"),
|
|
142
|
-
/** Single issue detail (modal) */
|
|
143
|
-
issue: defineRoute("/dashboard/:projectId/issues/:issueNumber"),
|
|
144
|
-
/** Issue test sessions page */
|
|
145
|
-
issueSessions: defineRoute("/dashboard/:projectId/issue-sessions"),
|
|
146
|
-
/** Single test session detail (modal) */
|
|
147
|
-
issueSession: defineRoute("/dashboard/:projectId/issue-sessions/:sessionId"),
|
|
148
|
-
/** Project settings page */
|
|
149
|
-
projectSettings: defineRoute("/dashboard/:projectId/settings"),
|
|
150
|
-
/** Schedules page */
|
|
151
|
-
schedules: defineRoute("/dashboard/:projectId/schedules"),
|
|
152
|
-
/** Flow charts page */
|
|
153
|
-
flowCharts: defineRoute("/dashboard/:projectId/flowcharts"),
|
|
154
|
-
/** Single flow chart detail (embedded in dashboard - deprecated, use flowChartView) */
|
|
155
|
-
flowChart: defineRoute("/dashboard/:projectId/flowcharts/:flowchartId"),
|
|
156
|
-
/** Full-page flow chart viewer (outside dashboard layout) */
|
|
157
|
-
flowChartView: defineRoute("/view/flowchart/:projectId/:flowchartId"),
|
|
158
|
-
// ============================================
|
|
159
|
-
// External/public routes
|
|
160
|
-
// ============================================
|
|
161
|
-
/** Organization invite acceptance page */
|
|
162
|
-
invite: defineRoute("/invite/:token"),
|
|
163
|
-
/** Quick start flow for new users (pre-auth) */
|
|
164
|
-
quickStart: defineRoute("/start"),
|
|
165
|
-
/** Public job view with token-secured access */
|
|
166
|
-
publicJob: defineRoute("/j/:jobId/:token"),
|
|
167
|
-
/** Public system status page */
|
|
168
|
-
statuspage: defineRoute("/statuspage"),
|
|
169
|
-
/** Documentation */
|
|
170
|
-
docs: defineRoute("/docs/setup"),
|
|
171
|
-
/** Pricing page */
|
|
172
|
-
pricing: defineRoute("/pricing"),
|
|
173
|
-
/** Home page */
|
|
174
|
-
home: defineRoute("/"),
|
|
175
|
-
// ============================================
|
|
176
|
-
// Learn Platform routes
|
|
177
|
-
// ============================================
|
|
178
|
-
/** Learn catalogue (default product) */
|
|
179
|
-
learn: defineRoute("/learn"),
|
|
180
|
-
/** Single lesson view */
|
|
181
|
-
learnLesson: defineRoute("/learn/:lessonSlug")
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
// ../shared/dist/routes/api-routes.js
|
|
185
|
-
var apiRoutes = {
|
|
186
|
-
// ============================================
|
|
187
|
-
// Jobs endpoints
|
|
188
|
-
// ============================================
|
|
189
|
-
/** List jobs (GET) or create job (POST) */
|
|
190
|
-
jobs: defineRoute("/jobs"),
|
|
191
|
-
/** Get/update/delete specific job */
|
|
192
|
-
job: defineRoute("/jobs/:jobId"),
|
|
193
|
-
/** Get job status (for polling) */
|
|
194
|
-
jobStatus: defineRoute("/jobs/:jobId/status"),
|
|
195
|
-
/** Get individual job artifact by type */
|
|
196
|
-
jobArtifact: defineRoute("/jobs/:jobId/artifacts/:artifactType"),
|
|
197
|
-
/** Update job feedback and rating */
|
|
198
|
-
jobFeedback: defineRoute("/jobs/:jobId/feedback"),
|
|
199
|
-
/** Enable/disable public sharing for a job (POST = enable, DELETE = disable) */
|
|
200
|
-
jobShare: defineRoute("/jobs/:jobId/share"),
|
|
201
|
-
/** Claim a job (for testers) */
|
|
202
|
-
jobClaim: defineRoute("/jobs/:jobId/claim"),
|
|
203
|
-
/** Synchronous job execution */
|
|
204
|
-
run: defineRoute("/run"),
|
|
205
|
-
/** Get public job (no auth required, token-secured) */
|
|
206
|
-
publicJob: defineRoute("/public/jobs/:jobId/:token"),
|
|
207
|
-
// ============================================
|
|
208
|
-
// Tester App Job endpoints (secured by testerToken)
|
|
209
|
-
// ============================================
|
|
210
|
-
/** Get/update job for tester app */
|
|
211
|
-
testerJob: defineRoute("/tester/jobs/:jobId"),
|
|
212
|
-
/** Get presigned upload URLs for artifacts */
|
|
213
|
-
testerJobUploadUrls: defineRoute("/tester/jobs/:jobId/upload-urls"),
|
|
214
|
-
/** Process test results with AI */
|
|
215
|
-
testerJobProcessResults: defineRoute("/tester/jobs/:jobId/process-results"),
|
|
216
|
-
/** Get processing status */
|
|
217
|
-
testerJobProcessingStatus: defineRoute("/tester/jobs/:jobId/processing/:processingJobId"),
|
|
218
|
-
/** Extract frames from video */
|
|
219
|
-
testerJobExtractFrames: defineRoute("/tester/jobs/:jobId/extract-frames"),
|
|
220
|
-
/** Abort job due to platform issue */
|
|
221
|
-
testerJobAbort: defineRoute("/tester/jobs/:jobId/abort"),
|
|
222
|
-
/** Mark job as invalid due to bad instructions */
|
|
223
|
-
testerJobInvalid: defineRoute("/tester/jobs/:jobId/invalid"),
|
|
224
|
-
/** Notify phase transition from tester app */
|
|
225
|
-
testerJobPhase: defineRoute("/tester/jobs/:jobId/phase"),
|
|
226
|
-
// ============================================
|
|
227
|
-
// LiveKit endpoints (screen sharing & recording)
|
|
228
|
-
// ============================================
|
|
229
|
-
/** Start LiveKit session + get tester token (testerToken auth) */
|
|
230
|
-
livekitTesterToken: defineRoute("/tester/jobs/:jobId/livekit-token"),
|
|
231
|
-
/** End LiveKit session + stop egress (testerToken auth) */
|
|
232
|
-
livekitEndSession: defineRoute("/tester/jobs/:jobId/livekit-end"),
|
|
233
|
-
/** Save transcript entries from Web Speech API (testerToken auth) */
|
|
234
|
-
livekitTranscript: defineRoute("/tester/jobs/:jobId/livekit-transcript"),
|
|
235
|
-
/** Get viewer token for live watching (Clerk JWT auth) */
|
|
236
|
-
livekitViewerToken: defineRoute("/jobs/:jobId/livekit-viewer-token"),
|
|
237
|
-
/** Check if LiveKit session is active (Clerk JWT or testerToken auth) */
|
|
238
|
-
livekitStatus: defineRoute("/jobs/:jobId/livekit-status"),
|
|
239
|
-
/** Get presigned recording URL (Clerk JWT auth) */
|
|
240
|
-
livekitRecording: defineRoute("/jobs/:jobId/livekit-recording"),
|
|
241
|
-
// ============================================
|
|
242
|
-
// API Keys endpoints
|
|
243
|
-
// ============================================
|
|
244
|
-
/** List API keys (GET) or create key (POST) */
|
|
245
|
-
keys: defineRoute("/keys"),
|
|
246
|
-
/** Get/update/delete specific key */
|
|
247
|
-
key: defineRoute("/keys/:keyId"),
|
|
248
|
-
/** Revoke an API key */
|
|
249
|
-
keyRevoke: defineRoute("/keys/:keyId/revoke"),
|
|
250
|
-
/** Get API key info from header (for authenticated requests) */
|
|
251
|
-
keyInfo: defineRoute("/key-info"),
|
|
252
|
-
// ============================================
|
|
253
|
-
// Personal Access Tokens (PATs) endpoints
|
|
254
|
-
// ============================================
|
|
255
|
-
/** List PATs (GET) or create PAT (POST) */
|
|
256
|
-
pats: defineRoute("/pats"),
|
|
257
|
-
/** Get/delete specific PAT */
|
|
258
|
-
pat: defineRoute("/pats/:patId"),
|
|
259
|
-
/** Revoke a PAT */
|
|
260
|
-
patRevoke: defineRoute("/pats/:patId/revoke"),
|
|
261
|
-
/** Get PAT info from header (for authenticated requests) */
|
|
262
|
-
patInfo: defineRoute("/pat-info"),
|
|
263
|
-
// ============================================
|
|
264
|
-
// Projects endpoints
|
|
265
|
-
// ============================================
|
|
266
|
-
/** List projects (GET) or create project (POST) */
|
|
267
|
-
projects: defineRoute("/projects"),
|
|
268
|
-
/** Get/update/delete specific project */
|
|
269
|
-
project: defineRoute("/projects/:projectId"),
|
|
270
|
-
/** Get jobs for a project */
|
|
271
|
-
projectJobs: defineRoute("/projects/:projectId/jobs"),
|
|
272
|
-
/** Get API keys for a project */
|
|
273
|
-
projectApiKeys: defineRoute("/projects/:projectId/api-keys"),
|
|
274
|
-
/** Transfer a project to another user or organization */
|
|
275
|
-
projectTransfer: defineRoute("/projects/:projectId/transfer"),
|
|
276
|
-
/** List templates for a project (GET) or create template (POST) */
|
|
277
|
-
projectTemplates: defineRoute("/projects/:projectId/templates"),
|
|
278
|
-
/** Get/update/delete specific template */
|
|
279
|
-
projectTemplate: defineRoute("/projects/:projectId/templates/:templateId"),
|
|
280
|
-
/** List flow charts for a project (GET) or upload flow chart (POST) */
|
|
281
|
-
projectFlowCharts: defineRoute("/projects/:projectId/flowcharts"),
|
|
282
|
-
/** Upload flow chart to a project */
|
|
283
|
-
projectFlowChartsUpload: defineRoute("/projects/:projectId/flowcharts/upload"),
|
|
284
|
-
/** Get/delete specific flow chart */
|
|
285
|
-
projectFlowChart: defineRoute("/projects/:projectId/flowcharts/:flowchartId"),
|
|
286
|
-
/** Get flow chart data */
|
|
287
|
-
projectFlowChartData: defineRoute("/projects/:projectId/flowcharts/:flowchartId/data"),
|
|
288
|
-
/** Chat with AI about a flow chart */
|
|
289
|
-
projectFlowChartChat: defineRoute("/projects/:projectId/flowcharts/:flowchartId/chat"),
|
|
290
|
-
/** Enable/disable public sharing for a flow chart */
|
|
291
|
-
projectFlowChartShare: defineRoute("/projects/:projectId/flowcharts/:flowchartId/share"),
|
|
292
|
-
/** List schedules for a project (GET) or create schedule (POST) */
|
|
293
|
-
projectSchedules: defineRoute("/projects/:projectId/schedules"),
|
|
294
|
-
/** Get/update/delete specific schedule */
|
|
295
|
-
projectSchedule: defineRoute("/projects/:projectId/schedules/:scheduleId"),
|
|
296
|
-
/** List execution history for a schedule */
|
|
297
|
-
projectScheduleExecutions: defineRoute("/projects/:projectId/schedules/:scheduleId/executions"),
|
|
298
|
-
/** Bulk create projects from GitHub repos */
|
|
299
|
-
bulkCreateProjects: defineRoute("/projects/bulk"),
|
|
300
|
-
// ============================================
|
|
301
|
-
// GitHub endpoints
|
|
302
|
-
// ============================================
|
|
303
|
-
/** GitHub OAuth authorize */
|
|
304
|
-
githubOAuthAuthorize: defineRoute("/github/oauth/authorize"),
|
|
305
|
-
/** GitHub OAuth callback */
|
|
306
|
-
githubCallback: defineRoute("/github/oauth/callback"),
|
|
307
|
-
/** Link GitHub account (get GitHub username via OAuth) */
|
|
308
|
-
githubLink: defineRoute("/github/link"),
|
|
309
|
-
/** List issues for a repo (by owner/repo) */
|
|
310
|
-
githubIssuesByRepo: defineRoute("/github/issues/:owner/:repo"),
|
|
311
|
-
/** List issues (by projectId query param) */
|
|
312
|
-
githubIssues: defineRoute("/github/issues"),
|
|
313
|
-
/** Get a single issue */
|
|
314
|
-
githubIssue: defineRoute("/github/issues/:issueNumber"),
|
|
315
|
-
/** Get comments for an issue */
|
|
316
|
-
githubIssueComments: defineRoute("/github/issues/:issueNumber/comments"),
|
|
317
|
-
/** Get labels for a repo */
|
|
318
|
-
githubIssueLabels: defineRoute("/github/issues/labels"),
|
|
319
|
-
/** Get assignees for a repo */
|
|
320
|
-
githubIssueAssignees: defineRoute("/github/issues/assignees"),
|
|
321
|
-
/** Test a single issue */
|
|
322
|
-
githubIssueTest: defineRoute("/github/issues/test"),
|
|
323
|
-
/** Bulk test multiple issues */
|
|
324
|
-
githubIssuesBulkTest: defineRoute("/github/issues/bulk-test"),
|
|
325
|
-
/** List test sessions */
|
|
326
|
-
githubTestSessions: defineRoute("/github/issues/test-sessions"),
|
|
327
|
-
/** Get/delete a test session */
|
|
328
|
-
githubTestSession: defineRoute("/github/issues/test-sessions/:sessionId"),
|
|
329
|
-
/** Mark a test session as seen */
|
|
330
|
-
githubTestSessionSeen: defineRoute("/github/issues/test-sessions/:sessionId/seen"),
|
|
331
|
-
/** Get counts of running and unseen test sessions */
|
|
332
|
-
githubTestSessionsCounts: defineRoute("/github/issues/test-sessions/counts"),
|
|
333
|
-
/** Bulk test GitHub issues (legacy route) */
|
|
334
|
-
githubBulkTest: defineRoute("/github/bulk-test"),
|
|
335
|
-
/** GitHub App webhooks (receives issue_comment events etc.) */
|
|
336
|
-
githubWebhooks: defineRoute("/github/webhooks"),
|
|
337
|
-
// ============================================
|
|
338
|
-
// Auth endpoints (Clerk-based)
|
|
339
|
-
// ============================================
|
|
340
|
-
/** Sync user data from Clerk after sign-in */
|
|
341
|
-
authSync: defineRoute("/auth/sync"),
|
|
342
|
-
/** Get current user info */
|
|
343
|
-
authMe: defineRoute("/auth/me"),
|
|
344
|
-
/** Mark user as startup (bonus tokens) */
|
|
345
|
-
authStartup: defineRoute("/auth/startup"),
|
|
346
|
-
/** Delete user account and all associated data */
|
|
347
|
-
authDeleteAccount: defineRoute("/auth/account"),
|
|
348
|
-
/** Preview account deletion impact (owned orgs, members, projects) */
|
|
349
|
-
authDeletionPreview: defineRoute("/auth/account/deletion-preview"),
|
|
350
|
-
// ============================================
|
|
351
|
-
// Billing endpoints (Polar-based credits)
|
|
352
|
-
// ============================================
|
|
353
|
-
/** Get user's credit balance from Polar */
|
|
354
|
-
billingBalance: defineRoute("/billing/balance"),
|
|
355
|
-
/** Check if user has available credits */
|
|
356
|
-
billingHasCredits: defineRoute("/billing/has-credits"),
|
|
357
|
-
/** Get a customer portal URL for managing billing */
|
|
358
|
-
billingPortal: defineRoute("/billing/portal"),
|
|
359
|
-
/** Create a checkout session for purchasing credits */
|
|
360
|
-
billingCheckout: defineRoute("/billing/checkout"),
|
|
361
|
-
/** Get active subscription details (tier, cycle, etc.) */
|
|
362
|
-
billingSubscription: defineRoute("/billing/subscription"),
|
|
363
|
-
/** Update subscription plan (change tier/cycle) */
|
|
364
|
-
billingChangePlan: defineRoute("/billing/change-plan"),
|
|
365
|
-
// ============================================
|
|
366
|
-
// Other endpoints
|
|
367
|
-
// ============================================
|
|
368
|
-
/** Health check */
|
|
369
|
-
health: defineRoute("/health"),
|
|
370
|
-
/** Detailed system status */
|
|
371
|
-
status: defineRoute("/status"),
|
|
372
|
-
/** List templates */
|
|
373
|
-
templates: defineRoute("/templates"),
|
|
374
|
-
/** Issue analyzer */
|
|
375
|
-
issueAnalyzer: defineRoute("/issue-analyzer"),
|
|
376
|
-
/** PR analyzer */
|
|
377
|
-
prAnalyzer: defineRoute("/pr-analyzer"),
|
|
378
|
-
/** Logs endpoint */
|
|
379
|
-
logs: defineRoute("/logs"),
|
|
380
|
-
// ============================================
|
|
381
|
-
// Relevant Issues endpoints
|
|
382
|
-
// ============================================
|
|
383
|
-
/** Run relevant issues discovery for user */
|
|
384
|
-
relevantIssuesDiscover: defineRoute("/relevant-issues/discover"),
|
|
385
|
-
/** Get latest cached relevant issues result */
|
|
386
|
-
relevantIssuesLatest: defineRoute("/relevant-issues/latest"),
|
|
387
|
-
// ============================================
|
|
388
|
-
// Onboarding endpoints
|
|
389
|
-
// ============================================
|
|
390
|
-
/** Get/update user's onboarding state */
|
|
391
|
-
onboarding: defineRoute("/onboarding"),
|
|
392
|
-
/** Mark onboarding as complete */
|
|
393
|
-
onboardingComplete: defineRoute("/onboarding/complete"),
|
|
394
|
-
/** Check if user needs onboarding */
|
|
395
|
-
onboardingCheck: defineRoute("/onboarding/check"),
|
|
396
|
-
// ============================================
|
|
397
|
-
// Search endpoints
|
|
398
|
-
// ============================================
|
|
399
|
-
/** Global search across jobs, projects, issues, sessions */
|
|
400
|
-
search: defineRoute("/search"),
|
|
401
|
-
// ============================================
|
|
402
|
-
// Tester Profile endpoints
|
|
403
|
-
// ============================================
|
|
404
|
-
/** Apply to become a tester */
|
|
405
|
-
testerApply: defineRoute("/tester/apply"),
|
|
406
|
-
/** Get/update own tester profile */
|
|
407
|
-
testerProfile: defineRoute("/tester/profile"),
|
|
408
|
-
/** Get public tester profile */
|
|
409
|
-
testerPublicProfile: defineRoute("/testers/:testerId/profile"),
|
|
410
|
-
/** Download tester app */
|
|
411
|
-
testerDownloadApp: defineRoute("/tester/download-app"),
|
|
412
|
-
/** Download browser extension */
|
|
413
|
-
testerDownloadExtension: defineRoute("/tester/download-extension"),
|
|
414
|
-
/** Download mobile tester app (Android APK) */
|
|
415
|
-
testerDownloadMobileApp: defineRoute("/tester/download-mobile-app"),
|
|
416
|
-
/** List available (unclaimed) jobs for testers */
|
|
417
|
-
testerAvailableJobs: defineRoute("/tester/jobs/available"),
|
|
418
|
-
/** Get presigned URL for avatar upload */
|
|
419
|
-
testerAvatarUploadUrl: defineRoute("/tester/profile/avatar-upload-url"),
|
|
420
|
-
/** Get tester app version */
|
|
421
|
-
testerAppVersion: defineRoute("/tester/app-version"),
|
|
422
|
-
/** Register/unregister Expo push token for mobile notifications */
|
|
423
|
-
testerPushToken: defineRoute("/tester/push-token"),
|
|
424
|
-
// ============================================
|
|
425
|
-
// Extension endpoints (browser extension)
|
|
426
|
-
// ============================================
|
|
427
|
-
/** List extension tokens (GET) or create token (POST) */
|
|
428
|
-
extensionTokens: defineRoute("/tester/extension-tokens"),
|
|
429
|
-
/** Revoke (DELETE) a specific extension token */
|
|
430
|
-
extensionToken: defineRoute("/tester/extension-tokens/:tokenId"),
|
|
431
|
-
/** WebSocket endpoint for extension data streaming */
|
|
432
|
-
extensionStream: defineRoute("/extension/stream"),
|
|
433
|
-
/** Correlate extension data to a test job's time window */
|
|
434
|
-
testerJobCorrelate: defineRoute("/tester/jobs/:jobId/correlate"),
|
|
435
|
-
/** Check if extension data is flowing for this tester (polling endpoint) */
|
|
436
|
-
testerJobDataStatus: defineRoute("/tester/jobs/:jobId/data-status"),
|
|
437
|
-
/** Create extension token for auto-configuring the extension from the test page */
|
|
438
|
-
testerJobExtensionToken: defineRoute("/tester/jobs/:jobId/extension-token"),
|
|
439
|
-
// ============================================
|
|
440
|
-
// Repo Templates endpoints
|
|
441
|
-
// ============================================
|
|
442
|
-
/** List templates from a GitHub repo's .runhuman/templates/ directory */
|
|
443
|
-
repoTemplates: defineRoute("/repos/:owner/:repo/templates"),
|
|
444
|
-
/** Get a single template from a GitHub repo */
|
|
445
|
-
repoTemplate: defineRoute("/repos/:owner/:repo/templates/:templateName"),
|
|
446
|
-
// ============================================
|
|
447
|
-
// Organizations endpoints
|
|
448
|
-
// ============================================
|
|
449
|
-
/** List organizations (GET) or create organization (POST) */
|
|
450
|
-
organizations: defineRoute("/organizations"),
|
|
451
|
-
/** Get/update/delete specific organization */
|
|
452
|
-
organization: defineRoute("/organizations/:organizationId"),
|
|
453
|
-
/** List organization members */
|
|
454
|
-
organizationMembers: defineRoute("/organizations/:organizationId/members"),
|
|
455
|
-
/** Invite member to organization */
|
|
456
|
-
organizationInvite: defineRoute("/organizations/:organizationId/invite"),
|
|
457
|
-
/** Remove member from organization */
|
|
458
|
-
organizationMember: defineRoute("/organizations/:organizationId/members/:userId"),
|
|
459
|
-
/** List organization projects */
|
|
460
|
-
organizationProjects: defineRoute("/organizations/:organizationId/projects"),
|
|
461
|
-
/** Get organization jobs (jobs across all projects in the organization) */
|
|
462
|
-
organizationJobs: defineRoute("/organizations/:organizationId/jobs"),
|
|
463
|
-
/** Transfer organization ownership */
|
|
464
|
-
organizationTransferOwnership: defineRoute("/organizations/:organizationId/transfer-ownership"),
|
|
465
|
-
/** Create organization API key */
|
|
466
|
-
organizationApiKeys: defineRoute("/organizations/:organizationId/api-keys"),
|
|
467
|
-
/** Get GitHub installations for an organization */
|
|
468
|
-
organizationGitHubInstallations: defineRoute("/organizations/:organizationId/github/installations"),
|
|
469
|
-
/** Get/delete specific GitHub installation for an organization */
|
|
470
|
-
organizationGitHubInstallation: defineRoute("/organizations/:organizationId/github/installations/:installationId"),
|
|
471
|
-
/** List repos accessible to an organization via its GitHub installations */
|
|
472
|
-
organizationGitHubRepos: defineRoute("/organizations/:organizationId/github/repos"),
|
|
473
|
-
/** Refresh repos for an organization's GitHub installation */
|
|
474
|
-
organizationGitHubInstallationRefresh: defineRoute("/organizations/:organizationId/github/installations/:installationId/refresh"),
|
|
475
|
-
/** Check if org has access to a specific repo via its GitHub installations */
|
|
476
|
-
organizationGitHubRepoCheckAccess: defineRoute("/organizations/:organizationId/github/repos/check-access"),
|
|
477
|
-
/** Find deployed URL for a repo via org's GitHub installations */
|
|
478
|
-
organizationGitHubRepoFindUrl: defineRoute("/organizations/:organizationId/github/repos/find-url"),
|
|
479
|
-
/** Get all schedules across projects in the organization */
|
|
480
|
-
organizationSchedules: defineRoute("/organizations/:organizationId/schedules"),
|
|
481
|
-
/** Get organization billing balance (Polar) */
|
|
482
|
-
organizationBilling: defineRoute("/organizations/:organizationId/billing"),
|
|
483
|
-
// ============================================
|
|
484
|
-
// Transfers endpoints
|
|
485
|
-
// ============================================
|
|
486
|
-
/** Get pending incoming transfers for current user */
|
|
487
|
-
transfersPending: defineRoute("/transfers/pending"),
|
|
488
|
-
/** Get pending outgoing transfers (initiated by current user) */
|
|
489
|
-
transfersOutgoing: defineRoute("/transfers/outgoing"),
|
|
490
|
-
/** Accept a transfer */
|
|
491
|
-
transferAccept: defineRoute("/transfers/:transferId/accept"),
|
|
492
|
-
/** Reject a transfer */
|
|
493
|
-
transferReject: defineRoute("/transfers/:transferId/reject"),
|
|
494
|
-
/** Cancel a transfer */
|
|
495
|
-
transferCancel: defineRoute("/transfers/:transferId/cancel"),
|
|
496
|
-
// ============================================
|
|
497
|
-
// Invites endpoints (public)
|
|
498
|
-
// ============================================
|
|
499
|
-
/** Get invite details by token (public) */
|
|
500
|
-
invite: defineRoute("/invites/:token"),
|
|
501
|
-
/** Redeem an invite (authenticated) */
|
|
502
|
-
inviteRedeem: defineRoute("/invites/:token/redeem"),
|
|
503
|
-
// ============================================
|
|
504
|
-
// User Agreements endpoints
|
|
505
|
-
// ============================================
|
|
506
|
-
/** Record user agreement acceptance (POST) or get user's agreements (GET) */
|
|
507
|
-
agreements: defineRoute("/agreements"),
|
|
508
|
-
/** Check if user has accepted a specific agreement */
|
|
509
|
-
agreementCheck: defineRoute("/agreements/check"),
|
|
510
|
-
// ============================================
|
|
511
|
-
// Organization SSO endpoints
|
|
512
|
-
// ============================================
|
|
513
|
-
/** List SSO connections for an organization (GET) or create one (POST) */
|
|
514
|
-
organizationSsoConnections: defineRoute("/organizations/:organizationId/sso/connections"),
|
|
515
|
-
/** Get/delete a specific SSO connection */
|
|
516
|
-
organizationSsoConnection: defineRoute("/organizations/:organizationId/sso/connections/:connectionId"),
|
|
517
|
-
// ============================================
|
|
518
|
-
// Enterprise endpoints
|
|
519
|
-
// ============================================
|
|
520
|
-
/** Submit enterprise contact inquiry (POST) */
|
|
521
|
-
enterpriseInquiry: defineRoute("/enterprise/inquiry"),
|
|
522
|
-
// ============================================
|
|
523
|
-
// Changelog endpoints
|
|
524
|
-
// ============================================
|
|
525
|
-
/** List changelog entries (public) */
|
|
526
|
-
changelog: defineRoute("/changelog"),
|
|
527
|
-
/** Get unread changelog count (authenticated) */
|
|
528
|
-
changelogUnreadCount: defineRoute("/changelog/unread-count"),
|
|
529
|
-
/** Mark changelog as read (authenticated) */
|
|
530
|
-
changelogMarkRead: defineRoute("/changelog/mark-read"),
|
|
531
|
-
// ============================================
|
|
532
|
-
// Telemetry SDK endpoints
|
|
533
|
-
// ============================================
|
|
534
|
-
/** Create telemetry session (POST) — SDK calls this when a test session starts */
|
|
535
|
-
telemetrySessions: defineRoute("/telemetry/sessions"),
|
|
536
|
-
/** End telemetry session (POST) — SDK calls this when testing completes */
|
|
537
|
-
telemetrySessionEnd: defineRoute("/telemetry/sessions/:sessionId/end"),
|
|
538
|
-
/** Submit telemetry event batch (POST) — SDK periodically flushes captured events */
|
|
539
|
-
telemetryBatch: defineRoute("/telemetry/sessions/:sessionId/events"),
|
|
540
|
-
/** Check session status for a job (GET) — SDK polls to know when to activate */
|
|
541
|
-
telemetrySessionStatus: defineRoute("/telemetry/jobs/:jobId/status"),
|
|
542
|
-
/** Resolve a sensor short code to a jobId (GET) — SDK calls this when tester enters a code */
|
|
543
|
-
telemetryShortCodeResolve: defineRoute("/telemetry/short-codes/:code")
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
// src/api-client.ts
|
|
547
|
-
var ApiClient = class {
|
|
548
|
-
constructor(baseUrl, apiKey) {
|
|
549
|
-
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
550
|
-
this.apiKey = apiKey;
|
|
551
|
-
this.fetchFn = globalThis.fetch.bind(globalThis);
|
|
552
|
-
}
|
|
553
|
-
async createSession(req) {
|
|
554
|
-
return this.post(
|
|
555
|
-
apiRoutes.telemetrySessions.build(),
|
|
556
|
-
req
|
|
557
|
-
);
|
|
558
|
-
}
|
|
559
|
-
async submitBatch(sessionId, req) {
|
|
560
|
-
return this.post(
|
|
561
|
-
apiRoutes.telemetryBatch.build({ sessionId }),
|
|
562
|
-
req
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
async endSession(sessionId) {
|
|
566
|
-
return this.post(
|
|
567
|
-
apiRoutes.telemetrySessionEnd.build({ sessionId }),
|
|
568
|
-
void 0
|
|
569
|
-
);
|
|
570
|
-
}
|
|
571
|
-
async getSessionStatus(jobId) {
|
|
572
|
-
return this.get(apiRoutes.telemetrySessionStatus.build({ jobId }));
|
|
573
|
-
}
|
|
574
|
-
async resolveShortCode(code) {
|
|
575
|
-
const normalized = code.replace(/^RH-/i, "").toUpperCase();
|
|
576
|
-
try {
|
|
577
|
-
return await this.get(
|
|
578
|
-
apiRoutes.telemetryShortCodeResolve.build({ code: normalized })
|
|
579
|
-
);
|
|
580
|
-
} catch (error) {
|
|
581
|
-
if (error instanceof Error && error.message.includes("404")) return null;
|
|
582
|
-
throw error;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
url(routePath) {
|
|
586
|
-
return `${this.baseUrl}${API_PREFIX}${routePath}`;
|
|
587
|
-
}
|
|
588
|
-
async post(routePath, body) {
|
|
589
|
-
const response = await this.fetchFn(this.url(routePath), {
|
|
590
|
-
method: "POST",
|
|
591
|
-
headers: {
|
|
592
|
-
"Content-Type": "application/json",
|
|
593
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
594
|
-
},
|
|
595
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
596
|
-
});
|
|
597
|
-
if (!response.ok) {
|
|
598
|
-
const text = await response.text();
|
|
599
|
-
throw new Error(`Runhuman API error ${response.status}: ${text}`);
|
|
600
|
-
}
|
|
601
|
-
return response.json();
|
|
602
|
-
}
|
|
603
|
-
async get(routePath) {
|
|
604
|
-
const response = await this.fetchFn(this.url(routePath), {
|
|
605
|
-
method: "GET",
|
|
606
|
-
headers: {
|
|
607
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
if (!response.ok) {
|
|
611
|
-
const text = await response.text();
|
|
612
|
-
throw new Error(`Runhuman API error ${response.status}: ${text}`);
|
|
613
|
-
}
|
|
614
|
-
return response.json();
|
|
615
|
-
}
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
// src/event-buffer.ts
|
|
619
|
-
var EventBuffer = class {
|
|
620
|
-
constructor(maxCapacity = 1e3) {
|
|
621
|
-
this.buffer = [];
|
|
622
|
-
this.maxCapacity = maxCapacity;
|
|
623
|
-
}
|
|
624
|
-
get size() {
|
|
625
|
-
return this.buffer.length;
|
|
626
|
-
}
|
|
627
|
-
push(event) {
|
|
628
|
-
if (this.buffer.length >= this.maxCapacity) {
|
|
629
|
-
this.buffer.shift();
|
|
630
|
-
}
|
|
631
|
-
this.buffer.push(event);
|
|
632
|
-
}
|
|
633
|
-
drain() {
|
|
634
|
-
const events = this.buffer;
|
|
635
|
-
this.buffer = [];
|
|
636
|
-
return events;
|
|
637
|
-
}
|
|
638
|
-
clear() {
|
|
639
|
-
this.buffer = [];
|
|
640
|
-
}
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
// src/interceptors/network.interceptor.ts
|
|
644
|
-
var STRIPPED_HEADERS = /* @__PURE__ */ new Set([
|
|
645
|
-
"authorization",
|
|
646
|
-
"cookie",
|
|
647
|
-
"set-cookie",
|
|
648
|
-
"x-api-key",
|
|
649
|
-
"proxy-authorization"
|
|
650
|
-
]);
|
|
651
|
-
function scrubHeaders(headers) {
|
|
652
|
-
const result = {};
|
|
653
|
-
headers.forEach((value, key) => {
|
|
654
|
-
result[key] = STRIPPED_HEADERS.has(key.toLowerCase()) ? "[REDACTED]" : value;
|
|
655
|
-
});
|
|
656
|
-
return result;
|
|
657
|
-
}
|
|
658
|
-
var NetworkInterceptor = class {
|
|
659
|
-
constructor() {
|
|
660
|
-
this.originalFetch = null;
|
|
661
|
-
this.sessionStartTime = 0;
|
|
662
|
-
}
|
|
663
|
-
install(onEvent) {
|
|
664
|
-
this.originalFetch = globalThis.fetch;
|
|
665
|
-
this.sessionStartTime = Date.now();
|
|
666
|
-
const captured = this.originalFetch;
|
|
667
|
-
const startTime = this.sessionStartTime;
|
|
668
|
-
globalThis.fetch = async function interceptedFetch(input, init) {
|
|
669
|
-
const requestStart = Date.now();
|
|
670
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
671
|
-
const method = init?.method ?? "GET";
|
|
672
|
-
const response = await captured.call(globalThis, input, init);
|
|
673
|
-
const event = {
|
|
674
|
-
type: "network",
|
|
675
|
-
relativeTimestampMs: requestStart - startTime,
|
|
676
|
-
clientTimestamp: new Date(requestStart).toISOString(),
|
|
677
|
-
data: {
|
|
678
|
-
url,
|
|
679
|
-
method: method.toUpperCase(),
|
|
680
|
-
status: response.status,
|
|
681
|
-
statusText: response.statusText,
|
|
682
|
-
durationMs: Date.now() - requestStart,
|
|
683
|
-
responseHeaders: scrubHeaders(response.headers)
|
|
684
|
-
}
|
|
685
|
-
};
|
|
686
|
-
onEvent(event);
|
|
687
|
-
return response;
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
uninstall() {
|
|
691
|
-
if (this.originalFetch) {
|
|
692
|
-
globalThis.fetch = this.originalFetch;
|
|
693
|
-
this.originalFetch = null;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
/** Update the session start time (called when a new session begins) */
|
|
697
|
-
setSessionStartTime(startTime) {
|
|
698
|
-
this.sessionStartTime = startTime;
|
|
699
|
-
}
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
// src/interceptors/console.interceptor.ts
|
|
703
|
-
var CONSOLE_METHODS = ["log", "warn", "error", "info", "debug"];
|
|
704
|
-
function stringify(args) {
|
|
705
|
-
return args.map((arg) => {
|
|
706
|
-
if (typeof arg === "string") return arg;
|
|
707
|
-
try {
|
|
708
|
-
return JSON.stringify(arg);
|
|
709
|
-
} catch {
|
|
710
|
-
return String(arg);
|
|
711
|
-
}
|
|
712
|
-
}).join(" ");
|
|
713
|
-
}
|
|
714
|
-
var ConsoleInterceptor = class {
|
|
715
|
-
constructor() {
|
|
716
|
-
this.originals = {};
|
|
717
|
-
this.sessionStartTime = 0;
|
|
718
|
-
}
|
|
719
|
-
install(onEvent) {
|
|
720
|
-
this.sessionStartTime = Date.now();
|
|
721
|
-
const startTime = this.sessionStartTime;
|
|
722
|
-
for (const method of CONSOLE_METHODS) {
|
|
723
|
-
this.originals[method] = console[method].bind(console);
|
|
724
|
-
console[method] = (...args) => {
|
|
725
|
-
const now = Date.now();
|
|
726
|
-
const event = {
|
|
727
|
-
type: "console",
|
|
728
|
-
relativeTimestampMs: now - startTime,
|
|
729
|
-
clientTimestamp: new Date(now).toISOString(),
|
|
730
|
-
data: {
|
|
731
|
-
level: method,
|
|
732
|
-
message: stringify(args)
|
|
733
|
-
}
|
|
734
|
-
};
|
|
735
|
-
onEvent(event);
|
|
736
|
-
this.originals[method](...args);
|
|
737
|
-
};
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
uninstall() {
|
|
741
|
-
for (const method of CONSOLE_METHODS) {
|
|
742
|
-
if (this.originals[method]) {
|
|
743
|
-
console[method] = this.originals[method];
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
this.originals = {};
|
|
747
|
-
}
|
|
748
|
-
setSessionStartTime(startTime) {
|
|
749
|
-
this.sessionStartTime = startTime;
|
|
750
|
-
}
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
// src/interceptors/error.interceptor.ts
|
|
754
|
-
var ErrorInterceptor = class {
|
|
755
|
-
constructor() {
|
|
756
|
-
this.previousHandler = null;
|
|
757
|
-
this.previousOnError = null;
|
|
758
|
-
this.sessionStartTime = 0;
|
|
759
|
-
}
|
|
760
|
-
install(onEvent) {
|
|
761
|
-
this.sessionStartTime = Date.now();
|
|
762
|
-
const startTime = this.sessionStartTime;
|
|
763
|
-
if (typeof ErrorUtils !== "undefined") {
|
|
764
|
-
this.previousHandler = ErrorUtils.getGlobalHandler();
|
|
765
|
-
const prev = this.previousHandler;
|
|
766
|
-
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
767
|
-
const now = Date.now();
|
|
768
|
-
const event = {
|
|
769
|
-
type: isFatal ? "crash" : "error",
|
|
770
|
-
relativeTimestampMs: now - startTime,
|
|
771
|
-
clientTimestamp: new Date(now).toISOString(),
|
|
772
|
-
data: {
|
|
773
|
-
message: error.message,
|
|
774
|
-
stack: error.stack,
|
|
775
|
-
name: error.name,
|
|
776
|
-
isFatal
|
|
777
|
-
}
|
|
778
|
-
};
|
|
779
|
-
onEvent(event);
|
|
780
|
-
prev(error, isFatal);
|
|
781
|
-
});
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
784
|
-
this.previousOnError = globalThis.onerror;
|
|
785
|
-
globalThis.onerror = (message, source, lineno, colno, error) => {
|
|
786
|
-
const now = Date.now();
|
|
787
|
-
const event = {
|
|
788
|
-
type: "error",
|
|
789
|
-
relativeTimestampMs: now - startTime,
|
|
790
|
-
clientTimestamp: new Date(now).toISOString(),
|
|
791
|
-
data: {
|
|
792
|
-
message: typeof message === "string" ? message : message.type,
|
|
793
|
-
stack: error?.stack,
|
|
794
|
-
name: error?.name,
|
|
795
|
-
source,
|
|
796
|
-
lineno,
|
|
797
|
-
colno
|
|
798
|
-
}
|
|
799
|
-
};
|
|
800
|
-
onEvent(event);
|
|
801
|
-
if (typeof this.previousOnError === "function") {
|
|
802
|
-
return this.previousOnError.call(globalThis, message, source, lineno, colno, error);
|
|
803
|
-
}
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
uninstall() {
|
|
807
|
-
if (typeof ErrorUtils !== "undefined" && this.previousHandler) {
|
|
808
|
-
ErrorUtils.setGlobalHandler(this.previousHandler);
|
|
809
|
-
this.previousHandler = null;
|
|
810
|
-
}
|
|
811
|
-
if (this.previousOnError !== null) {
|
|
812
|
-
globalThis.onerror = this.previousOnError;
|
|
813
|
-
this.previousOnError = null;
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
setSessionStartTime(startTime) {
|
|
817
|
-
this.sessionStartTime = startTime;
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
// src/interceptors/index.ts
|
|
822
|
-
var InterceptorManager = class {
|
|
823
|
-
constructor() {
|
|
824
|
-
this.network = new NetworkInterceptor();
|
|
825
|
-
this.console = new ConsoleInterceptor();
|
|
826
|
-
this.error = new ErrorInterceptor();
|
|
827
|
-
this.interceptors = [this.network, this.console, this.error];
|
|
828
|
-
}
|
|
829
|
-
install(onEvent) {
|
|
830
|
-
for (const interceptor of this.interceptors) {
|
|
831
|
-
interceptor.install(onEvent);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
uninstall() {
|
|
835
|
-
for (let i = this.interceptors.length - 1; i >= 0; i--) {
|
|
836
|
-
this.interceptors[i].uninstall();
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
setSessionStartTime(startTime) {
|
|
840
|
-
this.network.setSessionStartTime(startTime);
|
|
841
|
-
this.console.setSessionStartTime(startTime);
|
|
842
|
-
this.error.setSessionStartTime(startTime);
|
|
843
|
-
}
|
|
844
|
-
};
|
|
845
|
-
|
|
846
|
-
// src/session-manager.ts
|
|
847
|
-
var SessionManager = class {
|
|
848
|
-
constructor(config) {
|
|
849
|
-
this.state = "idle";
|
|
850
|
-
this.sessionId = null;
|
|
851
|
-
this.activeJobId = null;
|
|
852
|
-
this.pollTimer = null;
|
|
853
|
-
this.flushTimer = null;
|
|
854
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
855
|
-
this.config = config;
|
|
856
|
-
this.buffer = new EventBuffer(config.maxBufferSize);
|
|
857
|
-
this.interceptors = new InterceptorManager();
|
|
858
|
-
}
|
|
859
|
-
getState() {
|
|
860
|
-
return this.state;
|
|
861
|
-
}
|
|
862
|
-
/** Alias for getState() — named for React's useSyncExternalStore convention */
|
|
863
|
-
getSnapshot() {
|
|
864
|
-
return this.state;
|
|
865
|
-
}
|
|
866
|
-
getSessionId() {
|
|
867
|
-
return this.sessionId;
|
|
868
|
-
}
|
|
869
|
-
getActiveJobId() {
|
|
870
|
-
return this.activeJobId;
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* Subscribe to state changes. Returns an unsubscribe function.
|
|
874
|
-
* Compatible with React's useSyncExternalStore.
|
|
875
|
-
*/
|
|
876
|
-
subscribe(listener) {
|
|
877
|
-
this.listeners.add(listener);
|
|
878
|
-
return () => this.listeners.delete(listener);
|
|
879
|
-
}
|
|
880
|
-
setState(newState) {
|
|
881
|
-
this.state = newState;
|
|
882
|
-
for (const listener of this.listeners) {
|
|
883
|
-
listener();
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
/**
|
|
887
|
-
* Start polling for job activation.
|
|
888
|
-
* The sensor calls this on init — polling continues until a job is detected
|
|
889
|
-
* or stopPolling() is called.
|
|
890
|
-
*/
|
|
891
|
-
startPolling(jobId) {
|
|
892
|
-
if (this.state !== "idle") return;
|
|
893
|
-
this.activeJobId = jobId;
|
|
894
|
-
this.setState("polling");
|
|
895
|
-
this.log("Polling started", { jobId });
|
|
896
|
-
this.pollTimer = setInterval(() => this.pollOnce(), this.config.pollIntervalMs);
|
|
897
|
-
this.pollOnce();
|
|
898
|
-
}
|
|
899
|
-
stopPolling() {
|
|
900
|
-
if (this.pollTimer) {
|
|
901
|
-
clearInterval(this.pollTimer);
|
|
902
|
-
this.pollTimer = null;
|
|
903
|
-
}
|
|
904
|
-
if (this.state === "polling") {
|
|
905
|
-
this.setState("idle");
|
|
906
|
-
this.log("Polling stopped");
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Activate a session immediately (e.g., from a deep link).
|
|
911
|
-
* Skips polling — goes straight to session creation.
|
|
912
|
-
*/
|
|
913
|
-
async activate(jobId) {
|
|
914
|
-
if (this.state === "active") {
|
|
915
|
-
this.log("Already active, ignoring activate()", { jobId });
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
918
|
-
this.stopPolling();
|
|
919
|
-
this.activeJobId = jobId;
|
|
920
|
-
await this.startSession();
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* End the current session and return to idle.
|
|
924
|
-
*/
|
|
925
|
-
async deactivate() {
|
|
926
|
-
if (this.state !== "active") return;
|
|
927
|
-
await this.endSession();
|
|
928
|
-
}
|
|
929
|
-
/**
|
|
930
|
-
* Full teardown — stop everything and clean up.
|
|
931
|
-
*/
|
|
932
|
-
async destroy() {
|
|
933
|
-
this.stopPolling();
|
|
934
|
-
if (this.state === "active") {
|
|
935
|
-
await this.endSession();
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
async pollOnce() {
|
|
939
|
-
if (this.state !== "polling" || !this.activeJobId) return;
|
|
940
|
-
try {
|
|
941
|
-
const status = await this.config.apiClient.getSessionStatus(this.activeJobId);
|
|
942
|
-
if (status.jobActive && !status.activeSessionId) {
|
|
943
|
-
this.log("Job is active, creating session", {
|
|
944
|
-
jobId: this.activeJobId
|
|
945
|
-
});
|
|
946
|
-
this.stopPolling();
|
|
947
|
-
await this.startSession();
|
|
948
|
-
}
|
|
949
|
-
} catch (error) {
|
|
950
|
-
this.log("Poll failed", { error });
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
async startSession() {
|
|
954
|
-
this.setState("active");
|
|
955
|
-
const now = /* @__PURE__ */ new Date();
|
|
956
|
-
const response = await this.config.apiClient.createSession({
|
|
957
|
-
jobId: this.activeJobId,
|
|
958
|
-
platform: this.config.platform,
|
|
959
|
-
sdkVersion: this.config.sdkVersion,
|
|
960
|
-
clientStartTime: now.toISOString()
|
|
961
|
-
});
|
|
962
|
-
this.sessionId = response.sessionId;
|
|
963
|
-
const sessionStartTime = now.getTime();
|
|
964
|
-
this.log("Session started", {
|
|
965
|
-
sessionId: this.sessionId,
|
|
966
|
-
clockOffsetMs: response.clockOffsetMs
|
|
967
|
-
});
|
|
968
|
-
this.interceptors.setSessionStartTime(sessionStartTime);
|
|
969
|
-
this.interceptors.install((event) => this.buffer.push(event));
|
|
970
|
-
this.flushTimer = setInterval(() => this.flush(), this.config.flushIntervalMs);
|
|
971
|
-
}
|
|
972
|
-
async endSession() {
|
|
973
|
-
this.setState("ending");
|
|
974
|
-
if (this.flushTimer) {
|
|
975
|
-
clearInterval(this.flushTimer);
|
|
976
|
-
this.flushTimer = null;
|
|
977
|
-
}
|
|
978
|
-
this.interceptors.uninstall();
|
|
979
|
-
await this.flush();
|
|
980
|
-
if (this.sessionId) {
|
|
981
|
-
try {
|
|
982
|
-
const result = await this.config.apiClient.endSession(this.sessionId);
|
|
983
|
-
this.log("Session ended", {
|
|
984
|
-
sessionId: this.sessionId,
|
|
985
|
-
eventCount: result.eventCount,
|
|
986
|
-
correlated: result.correlated
|
|
987
|
-
});
|
|
988
|
-
} catch (error) {
|
|
989
|
-
this.log("Failed to end session", { sessionId: this.sessionId, error });
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
this.sessionId = null;
|
|
993
|
-
this.activeJobId = null;
|
|
994
|
-
this.buffer.clear();
|
|
995
|
-
this.setState("idle");
|
|
996
|
-
}
|
|
997
|
-
async flush() {
|
|
998
|
-
if (!this.sessionId) return;
|
|
999
|
-
const events = this.buffer.drain();
|
|
1000
|
-
if (events.length === 0) return;
|
|
1001
|
-
try {
|
|
1002
|
-
const result = await this.config.apiClient.submitBatch(this.sessionId, { events });
|
|
1003
|
-
this.log("Flushed events", {
|
|
1004
|
-
accepted: result.accepted,
|
|
1005
|
-
sessionEventCount: result.sessionEventCount
|
|
1006
|
-
});
|
|
1007
|
-
} catch (error) {
|
|
1008
|
-
this.log("Flush failed, events lost", { count: events.length, error });
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
log(message, data) {
|
|
1012
|
-
if (this.config.debug) {
|
|
1013
|
-
console.debug(`[Runhuman] ${message}`, data);
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
};
|
|
1017
|
-
|
|
1018
|
-
// src/deep-link-handler.ts
|
|
1019
|
-
var DeepLinkHandler = class {
|
|
1020
|
-
constructor(sessionManager, debug) {
|
|
1021
|
-
this.subscription = null;
|
|
1022
|
-
this.sessionManager = sessionManager;
|
|
1023
|
-
this.debug = debug;
|
|
1024
|
-
}
|
|
1025
|
-
async install() {
|
|
1026
|
-
const Linking = this.getLinking();
|
|
1027
|
-
if (!Linking) {
|
|
1028
|
-
this.log("react-native Linking not available, deep links disabled");
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
this.subscription = Linking.addEventListener("url", ({ url }) => {
|
|
1032
|
-
this.handleUrl(url);
|
|
1033
|
-
});
|
|
1034
|
-
const initialUrl = await Linking.getInitialURL();
|
|
1035
|
-
if (initialUrl) {
|
|
1036
|
-
this.handleUrl(initialUrl);
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
uninstall() {
|
|
1040
|
-
if (this.subscription) {
|
|
1041
|
-
this.subscription.remove();
|
|
1042
|
-
this.subscription = null;
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
handleUrl(url) {
|
|
1046
|
-
const jobId = this.extractJobId(url);
|
|
1047
|
-
if (!jobId) return;
|
|
1048
|
-
this.log("Deep link received", { url, jobId });
|
|
1049
|
-
this.sessionManager.activate(jobId);
|
|
1050
|
-
}
|
|
1051
|
-
/**
|
|
1052
|
-
* Extract jobId from a URL matching: <scheme>://runhuman?jobId=<id>
|
|
1053
|
-
* Returns null if the URL doesn't match the expected pattern.
|
|
1054
|
-
*/
|
|
1055
|
-
extractJobId(url) {
|
|
1056
|
-
try {
|
|
1057
|
-
const parsed = new URL(url);
|
|
1058
|
-
const isRunhuman = parsed.hostname === "runhuman" || parsed.pathname.includes("runhuman");
|
|
1059
|
-
if (!isRunhuman) return null;
|
|
1060
|
-
return parsed.searchParams.get("jobId");
|
|
1061
|
-
} catch {
|
|
1062
|
-
return null;
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
getLinking() {
|
|
1066
|
-
try {
|
|
1067
|
-
const { Linking } = require("react-native");
|
|
1068
|
-
return Linking;
|
|
1069
|
-
} catch {
|
|
1070
|
-
return null;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
log(message, data) {
|
|
1074
|
-
if (this.debug) {
|
|
1075
|
-
console.debug(`[Runhuman] ${message}`, data);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
};
|
|
1079
|
-
|
|
1080
|
-
// src/runhuman.ts
|
|
1081
|
-
var PRODUCTION_BASE_URL = "https://qa-experiment.fly.dev";
|
|
1082
|
-
var SDK_VERSION = "0.1.0";
|
|
1083
|
-
var INSTANCE_KEY = "__runhuman_sensor_instance__";
|
|
1084
|
-
var Runhuman = class _Runhuman {
|
|
1085
|
-
static get instance() {
|
|
1086
|
-
return globalThis[INSTANCE_KEY] ?? null;
|
|
1087
|
-
}
|
|
1088
|
-
static set instance(value) {
|
|
1089
|
-
globalThis[INSTANCE_KEY] = value;
|
|
1090
|
-
}
|
|
1091
|
-
constructor(config) {
|
|
1092
|
-
this.debug = config.debug === true;
|
|
1093
|
-
this.apiClient = new ApiClient(
|
|
1094
|
-
config.baseUrl ?? PRODUCTION_BASE_URL,
|
|
1095
|
-
config.apiKey
|
|
1096
|
-
);
|
|
1097
|
-
this.sessionManager = new SessionManager({
|
|
1098
|
-
apiClient: this.apiClient,
|
|
1099
|
-
platform: config.platform ?? "react-native",
|
|
1100
|
-
sdkVersion: SDK_VERSION,
|
|
1101
|
-
flushIntervalMs: config.flushIntervalMs ?? 5e3,
|
|
1102
|
-
pollIntervalMs: config.pollIntervalMs ?? 1e4,
|
|
1103
|
-
maxBufferSize: config.maxBufferSize ?? 1e3,
|
|
1104
|
-
debug: this.debug
|
|
1105
|
-
});
|
|
1106
|
-
const enableDeepLinks = config.enableDeepLinks !== false;
|
|
1107
|
-
if (enableDeepLinks) {
|
|
1108
|
-
this.deepLinkHandler = new DeepLinkHandler(this.sessionManager, this.debug);
|
|
1109
|
-
this.deepLinkHandler.install();
|
|
1110
|
-
} else {
|
|
1111
|
-
this.deepLinkHandler = null;
|
|
1112
|
-
}
|
|
1113
|
-
this.log("Initialized");
|
|
1114
|
-
if (config.jobId) {
|
|
1115
|
-
this.sessionManager.startPolling(config.jobId);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
/**
|
|
1119
|
-
* Initialize the Runhuman sensor. Call once at app startup.
|
|
1120
|
-
*
|
|
1121
|
-
* @example
|
|
1122
|
-
* ```ts
|
|
1123
|
-
* Runhuman.init({ apiKey: 'rh_your_api_key' });
|
|
1124
|
-
* ```
|
|
1125
|
-
*/
|
|
1126
|
-
static init(config) {
|
|
1127
|
-
if (_Runhuman.instance) {
|
|
1128
|
-
throw new Error("Runhuman.init() called twice \u2014 use Runhuman.getInstance() or call destroy() first");
|
|
1129
|
-
}
|
|
1130
|
-
_Runhuman.instance = new _Runhuman(config);
|
|
1131
|
-
return _Runhuman.instance;
|
|
1132
|
-
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Get the existing Runhuman instance.
|
|
1135
|
-
* Throws if init() hasn't been called.
|
|
1136
|
-
*/
|
|
1137
|
-
static getInstance() {
|
|
1138
|
-
if (!_Runhuman.instance) {
|
|
1139
|
-
throw new Error("Runhuman not initialized \u2014 call Runhuman.init() first");
|
|
1140
|
-
}
|
|
1141
|
-
return _Runhuman.instance;
|
|
1142
|
-
}
|
|
1143
|
-
/**
|
|
1144
|
-
* Manually activate a telemetry session for a specific job.
|
|
1145
|
-
* Use this when you receive a deep link or know the job ID.
|
|
1146
|
-
*/
|
|
1147
|
-
activate(jobId) {
|
|
1148
|
-
this.sessionManager.activate(jobId);
|
|
1149
|
-
}
|
|
1150
|
-
/**
|
|
1151
|
-
* Manually deactivate the current session.
|
|
1152
|
-
* Flushes remaining events and ends the session.
|
|
1153
|
-
*/
|
|
1154
|
-
async deactivate() {
|
|
1155
|
-
await this.sessionManager.deactivate();
|
|
1156
|
-
}
|
|
1157
|
-
/**
|
|
1158
|
-
* Full teardown — stops polling, ends any active session, cleans up listeners.
|
|
1159
|
-
* After calling destroy(), you can call init() again.
|
|
1160
|
-
*/
|
|
1161
|
-
async destroy() {
|
|
1162
|
-
await this.sessionManager.destroy();
|
|
1163
|
-
if (this.deepLinkHandler) {
|
|
1164
|
-
this.deepLinkHandler.uninstall();
|
|
1165
|
-
}
|
|
1166
|
-
_Runhuman.instance = null;
|
|
1167
|
-
this.log("Destroyed");
|
|
1168
|
-
}
|
|
1169
|
-
/** Access the session manager (used by <RunhumanOverlay /> for reactive state) */
|
|
1170
|
-
getSessionManager() {
|
|
1171
|
-
return this.sessionManager;
|
|
1172
|
-
}
|
|
1173
|
-
/** Access the API client (used by <RunhumanOverlay /> for short code resolution) */
|
|
1174
|
-
getApiClient() {
|
|
1175
|
-
return this.apiClient;
|
|
1176
|
-
}
|
|
1177
|
-
log(message) {
|
|
1178
|
-
if (this.debug) {
|
|
1179
|
-
console.debug(`[Runhuman] ${message}`);
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
};
|
|
1183
|
-
|
|
1184
|
-
// src/overlay/use-sensor-state.ts
|
|
1185
|
-
function subscribe(onStoreChange) {
|
|
1186
|
-
const sm = Runhuman.getInstance().getSessionManager();
|
|
1187
|
-
return sm.subscribe(onStoreChange);
|
|
1188
|
-
}
|
|
1189
|
-
function getSnapshot() {
|
|
1190
|
-
const sm = Runhuman.getInstance().getSessionManager();
|
|
1191
|
-
return {
|
|
1192
|
-
state: sm.getSnapshot(),
|
|
1193
|
-
activeJobId: sm.getActiveJobId(),
|
|
1194
|
-
sessionId: sm.getSessionId()
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
function useSensorState() {
|
|
1198
|
-
return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
// src/overlay/CodeEntryPanel.tsx
|
|
1202
|
-
var import_react2 = require("react");
|
|
1203
|
-
var import_react_native2 = require("react-native");
|
|
1204
|
-
|
|
1205
|
-
// src/overlay/overlay-styles.ts
|
|
1206
|
-
var import_react_native = require("react-native");
|
|
1207
|
-
var BRAND_GREEN = "#22c55e";
|
|
1208
|
-
var BRAND_AMBER = "#f59e0b";
|
|
1209
|
-
var BRAND_RED = "#ef4444";
|
|
1210
|
-
var BRAND_BLUE = "#3b82f6";
|
|
1211
|
-
var OVERLAY_BG = "rgba(0, 0, 0, 0.85)";
|
|
1212
|
-
var OVERLAY_BORDER = "rgba(255, 255, 255, 0.15)";
|
|
1213
|
-
var overlayStyles = import_react_native.StyleSheet.create({
|
|
1214
|
-
// Positioning containers
|
|
1215
|
-
topLeft: { top: 60, left: 16 },
|
|
1216
|
-
topRight: { top: 60, right: 16 },
|
|
1217
|
-
bottomLeft: { bottom: 40, left: 16 },
|
|
1218
|
-
bottomRight: { bottom: 40, right: 16 },
|
|
1219
|
-
// Code entry panel
|
|
1220
|
-
panel: {
|
|
1221
|
-
position: "absolute",
|
|
1222
|
-
zIndex: 99999,
|
|
1223
|
-
backgroundColor: OVERLAY_BG,
|
|
1224
|
-
borderRadius: 12,
|
|
1225
|
-
borderWidth: 1,
|
|
1226
|
-
borderColor: OVERLAY_BORDER,
|
|
1227
|
-
padding: 16,
|
|
1228
|
-
width: 220
|
|
1229
|
-
},
|
|
1230
|
-
panelTitle: {
|
|
1231
|
-
color: "#ffffff",
|
|
1232
|
-
fontSize: 13,
|
|
1233
|
-
fontWeight: "600",
|
|
1234
|
-
marginBottom: 8
|
|
1235
|
-
},
|
|
1236
|
-
input: {
|
|
1237
|
-
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
1238
|
-
borderRadius: 8,
|
|
1239
|
-
borderWidth: 1,
|
|
1240
|
-
borderColor: OVERLAY_BORDER,
|
|
1241
|
-
color: "#ffffff",
|
|
1242
|
-
fontSize: 18,
|
|
1243
|
-
fontFamily: "monospace",
|
|
1244
|
-
fontWeight: "bold",
|
|
1245
|
-
letterSpacing: 2,
|
|
1246
|
-
padding: 10,
|
|
1247
|
-
textAlign: "center"
|
|
1248
|
-
},
|
|
1249
|
-
inputError: {
|
|
1250
|
-
borderColor: BRAND_RED
|
|
1251
|
-
},
|
|
1252
|
-
submitButton: {
|
|
1253
|
-
backgroundColor: BRAND_BLUE,
|
|
1254
|
-
borderRadius: 8,
|
|
1255
|
-
paddingVertical: 8,
|
|
1256
|
-
marginTop: 8,
|
|
1257
|
-
alignItems: "center"
|
|
1258
|
-
},
|
|
1259
|
-
submitButtonDisabled: {
|
|
1260
|
-
opacity: 0.5
|
|
1261
|
-
},
|
|
1262
|
-
submitButtonText: {
|
|
1263
|
-
color: "#ffffff",
|
|
1264
|
-
fontSize: 13,
|
|
1265
|
-
fontWeight: "600"
|
|
1266
|
-
},
|
|
1267
|
-
errorText: {
|
|
1268
|
-
color: BRAND_RED,
|
|
1269
|
-
fontSize: 11,
|
|
1270
|
-
marginTop: 4,
|
|
1271
|
-
textAlign: "center"
|
|
1272
|
-
},
|
|
1273
|
-
minimizeButton: {
|
|
1274
|
-
position: "absolute",
|
|
1275
|
-
top: 8,
|
|
1276
|
-
right: 8
|
|
1277
|
-
},
|
|
1278
|
-
minimizeText: {
|
|
1279
|
-
color: "rgba(255, 255, 255, 0.5)",
|
|
1280
|
-
fontSize: 16
|
|
1281
|
-
},
|
|
1282
|
-
// Minimized fab (floating action button)
|
|
1283
|
-
fab: {
|
|
1284
|
-
position: "absolute",
|
|
1285
|
-
zIndex: 99999,
|
|
1286
|
-
width: 40,
|
|
1287
|
-
height: 40,
|
|
1288
|
-
borderRadius: 20,
|
|
1289
|
-
backgroundColor: OVERLAY_BG,
|
|
1290
|
-
borderWidth: 1,
|
|
1291
|
-
borderColor: OVERLAY_BORDER,
|
|
1292
|
-
alignItems: "center",
|
|
1293
|
-
justifyContent: "center"
|
|
1294
|
-
},
|
|
1295
|
-
fabText: {
|
|
1296
|
-
color: "#ffffff",
|
|
1297
|
-
fontSize: 16
|
|
1298
|
-
},
|
|
1299
|
-
// Active indicator dot
|
|
1300
|
-
indicator: {
|
|
1301
|
-
position: "absolute",
|
|
1302
|
-
zIndex: 99999,
|
|
1303
|
-
width: 12,
|
|
1304
|
-
height: 12,
|
|
1305
|
-
borderRadius: 6
|
|
1306
|
-
},
|
|
1307
|
-
indicatorActive: {
|
|
1308
|
-
backgroundColor: BRAND_GREEN
|
|
1309
|
-
},
|
|
1310
|
-
indicatorEnding: {
|
|
1311
|
-
backgroundColor: BRAND_AMBER
|
|
1312
|
-
},
|
|
1313
|
-
indicatorPolling: {
|
|
1314
|
-
backgroundColor: BRAND_BLUE
|
|
1315
|
-
}
|
|
1316
|
-
});
|
|
1317
|
-
|
|
1318
|
-
// src/overlay/CodeEntryPanel.tsx
|
|
1319
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1320
|
-
function CodeEntryPanel({ position }) {
|
|
1321
|
-
const [code, setCode] = (0, import_react2.useState)("");
|
|
1322
|
-
const [error, setError] = (0, import_react2.useState)(null);
|
|
1323
|
-
const [resolving, setResolving] = (0, import_react2.useState)(false);
|
|
1324
|
-
const [minimized, setMinimized] = (0, import_react2.useState)(false);
|
|
1325
|
-
const handleSubmit = async () => {
|
|
1326
|
-
const trimmed = code.trim();
|
|
1327
|
-
if (!trimmed) return;
|
|
1328
|
-
setResolving(true);
|
|
1329
|
-
setError(null);
|
|
1330
|
-
const instance = Runhuman.getInstance();
|
|
1331
|
-
const result = await instance.getApiClient().resolveShortCode(trimmed);
|
|
1332
|
-
if (result) {
|
|
1333
|
-
instance.activate(result.jobId);
|
|
1334
|
-
} else {
|
|
1335
|
-
setError("Invalid or expired code");
|
|
1336
|
-
setResolving(false);
|
|
1337
|
-
}
|
|
1338
|
-
};
|
|
1339
|
-
if (minimized) {
|
|
1340
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Pressable, { style: [overlayStyles.fab, overlayStyles[position]], onPress: () => setMinimized(false), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.fabText, children: "RH" }) });
|
|
1341
|
-
}
|
|
1342
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: [overlayStyles.panel, overlayStyles[position]], children: [
|
|
1343
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Pressable, { style: overlayStyles.minimizeButton, onPress: () => setMinimized(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.minimizeText, children: "-" }) }),
|
|
1344
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.panelTitle, children: "Runhuman Sensor" }),
|
|
1345
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1346
|
-
import_react_native2.TextInput,
|
|
1347
|
-
{
|
|
1348
|
-
style: error ? { ...overlayStyles.input, ...overlayStyles.inputError } : overlayStyles.input,
|
|
1349
|
-
value: code,
|
|
1350
|
-
onChangeText: (text) => {
|
|
1351
|
-
setCode(text.toUpperCase());
|
|
1352
|
-
setError(null);
|
|
1353
|
-
},
|
|
1354
|
-
placeholder: "RH-XXXX",
|
|
1355
|
-
placeholderTextColor: "rgba(255,255,255,0.3)",
|
|
1356
|
-
autoCapitalize: "characters",
|
|
1357
|
-
maxLength: 10,
|
|
1358
|
-
editable: !resolving,
|
|
1359
|
-
onSubmitEditing: handleSubmit
|
|
1360
|
-
}
|
|
1361
|
-
),
|
|
1362
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.errorText, children: error }) : null,
|
|
1363
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1364
|
-
import_react_native2.Pressable,
|
|
1365
|
-
{
|
|
1366
|
-
style: resolving ? { ...overlayStyles.submitButton, ...overlayStyles.submitButtonDisabled } : overlayStyles.submitButton,
|
|
1367
|
-
onPress: handleSubmit,
|
|
1368
|
-
disabled: resolving || !code.trim(),
|
|
1369
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.submitButtonText, children: resolving ? "Activating..." : "Activate" })
|
|
1370
|
-
}
|
|
1371
|
-
)
|
|
1372
|
-
] });
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
// src/overlay/ActiveIndicator.tsx
|
|
1376
|
-
var import_react3 = require("react");
|
|
1377
|
-
var import_react_native3 = require("react-native");
|
|
1378
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1379
|
-
var stateStyle = {
|
|
1380
|
-
active: overlayStyles.indicatorActive,
|
|
1381
|
-
ending: overlayStyles.indicatorEnding,
|
|
1382
|
-
polling: overlayStyles.indicatorPolling
|
|
1383
|
-
};
|
|
1384
|
-
function ActiveIndicator({ state, position }) {
|
|
1385
|
-
const pulse = (0, import_react3.useRef)(new import_react_native3.Animated.Value(1)).current;
|
|
1386
|
-
(0, import_react3.useEffect)(() => {
|
|
1387
|
-
if (state === "active") {
|
|
1388
|
-
const animation = import_react_native3.Animated.loop(
|
|
1389
|
-
import_react_native3.Animated.sequence([
|
|
1390
|
-
import_react_native3.Animated.timing(pulse, { toValue: 0.3, duration: 800, useNativeDriver: true }),
|
|
1391
|
-
import_react_native3.Animated.timing(pulse, { toValue: 1, duration: 800, useNativeDriver: true })
|
|
1392
|
-
])
|
|
1393
|
-
);
|
|
1394
|
-
animation.start();
|
|
1395
|
-
return () => animation.stop();
|
|
1396
|
-
}
|
|
1397
|
-
pulse.setValue(1);
|
|
1398
|
-
}, [state, pulse]);
|
|
1399
|
-
const colorStyle = stateStyle[state];
|
|
1400
|
-
if (!colorStyle) return null;
|
|
1401
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Animated.View, { style: [overlayStyles.indicator, overlayStyles[position], colorStyle, { opacity: pulse }] });
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
// src/overlay/RunhumanOverlay.tsx
|
|
1405
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1406
|
-
var positionMap = {
|
|
1407
|
-
"top-left": "topLeft",
|
|
1408
|
-
"top-right": "topRight",
|
|
1409
|
-
"bottom-left": "bottomLeft",
|
|
1410
|
-
"bottom-right": "bottomRight"
|
|
1411
|
-
};
|
|
1412
|
-
function RunhumanOverlay({ children, position = "bottom-right" }) {
|
|
1413
|
-
const { state, activeJobId } = useSensorState();
|
|
1414
|
-
const posKey = positionMap[position];
|
|
1415
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles.container, children: [
|
|
1416
|
-
children,
|
|
1417
|
-
state === "idle" && !activeJobId ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CodeEntryPanel, { position: posKey }) : null,
|
|
1418
|
-
state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActiveIndicator, { state, position: posKey }) : null
|
|
1419
|
-
] });
|
|
1420
|
-
}
|
|
1421
|
-
var styles = import_react_native4.StyleSheet.create({
|
|
1422
|
-
container: {
|
|
1423
|
-
flex: 1
|
|
1424
|
-
}
|
|
1425
|
-
});
|
|
1426
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1427
|
-
0 && (module.exports = {
|
|
1428
|
-
RunhumanOverlay,
|
|
1429
|
-
useSensorState
|
|
1430
|
-
});
|
|
1431
|
-
//# sourceMappingURL=index.js.map
|