remote-codex 0.11.3 → 0.11.5

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 (36) hide show
  1. package/README.md +4 -0
  2. package/apps/relay-server/dist/index.js +51 -2
  3. package/apps/supervisor-api/dist/chunk-ZWZQVPDT.js +27893 -0
  4. package/apps/supervisor-api/dist/index.js +4 -25727
  5. package/apps/supervisor-api/dist/worker-index.d.ts +2 -0
  6. package/apps/supervisor-api/dist/worker-index.js +197 -0
  7. package/apps/supervisor-web/dist/assets/index-CbdWtyx0.js +5 -0
  8. package/apps/supervisor-web/dist/assets/index-Di1JBevU.css +1 -0
  9. package/apps/supervisor-web/dist/assets/thread-ui-ICfwCbte.js +3604 -0
  10. package/apps/supervisor-web/dist/assets/ui-vendor-D1uxdi-d.js +430 -0
  11. package/apps/supervisor-web/dist/index.html +6 -7
  12. package/bin/remote-codex.mjs +534 -19
  13. package/package.json +41 -2
  14. package/packages/agent-runtime/src/types.ts +2 -1
  15. package/packages/codex/src/appServerManager.ts +1 -0
  16. package/packages/codex/src/historyItems.test.ts +45 -0
  17. package/packages/codex/src/historyItems.ts +22 -0
  18. package/packages/codex/src/runtimeAdapter.ts +6 -0
  19. package/packages/codex/src/types.ts +2 -1
  20. package/packages/db/migrations/0018_control_plane.sql +129 -0
  21. package/packages/db/migrations/0019_control_plane_projects.sql +19 -0
  22. package/packages/db/migrations/0020_control_workspace_status.sql +1 -0
  23. package/packages/db/migrations/0021_control_sandbox_lifecycle_fields.sql +3 -0
  24. package/packages/db/migrations/0022_control_sandbox_resource_profile.sql +1 -0
  25. package/packages/db/migrations/0023_control_usage_import_state.sql +18 -0
  26. package/packages/db/migrations/0024_control_auth.sql +23 -0
  27. package/packages/db/migrations/0025_control_harness_credentials.sql +29 -0
  28. package/packages/db/migrations/0026_control_harness_usage_events.sql +27 -0
  29. package/packages/db/src/schema.ts +305 -1
  30. package/packages/shared/src/index.ts +32 -0
  31. package/packages/shared/src/tokens.ts +137 -0
  32. package/apps/supervisor-web/dist/assets/index-CBIze1VS.css +0 -1
  33. package/apps/supervisor-web/dist/assets/index-YpGAPjED.js +0 -4
  34. package/apps/supervisor-web/dist/assets/thread-ui-BEieA99i.css +0 -1
  35. package/apps/supervisor-web/dist/assets/thread-ui-CF80LEEN.js +0 -3613
  36. package/apps/supervisor-web/dist/assets/ui-vendor-CW6egZBG.js +0 -430
@@ -1,4 +1,4 @@
1
- import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
1
+ import { integer, real, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
2
2
 
3
3
  export const hosts = sqliteTable('hosts', {
4
4
  id: text('id').primaryKey(),
@@ -176,3 +176,307 @@ export const policies = sqliteTable('policies', {
176
176
  createdAt: text('created_at').notNull(),
177
177
  updatedAt: text('updated_at').notNull()
178
178
  });
179
+
180
+ export const controlUsers = sqliteTable(
181
+ 'control_users',
182
+ {
183
+ id: text('id').primaryKey(),
184
+ authProvider: text('auth_provider').notNull(),
185
+ authSubject: text('auth_subject').notNull(),
186
+ email: text('email').notNull(),
187
+ displayName: text('display_name'),
188
+ status: text('status').notNull().default('active'),
189
+ plan: text('plan').notNull().default('developer'),
190
+ billingCustomerId: text('billing_customer_id'),
191
+ quotaProfile: text('quota_profile').notNull().default('developer'),
192
+ createdAt: text('created_at').notNull(),
193
+ updatedAt: text('updated_at').notNull(),
194
+ lastSeenAt: text('last_seen_at'),
195
+ },
196
+ (table) => ({
197
+ authSubjectUnique: uniqueIndex('control_users_auth_subject_idx').on(
198
+ table.authProvider,
199
+ table.authSubject,
200
+ ),
201
+ }),
202
+ );
203
+
204
+ export const controlAuthIdentities = sqliteTable(
205
+ 'control_auth_identities',
206
+ {
207
+ id: text('id').primaryKey(),
208
+ userId: text('user_id').notNull(),
209
+ authProvider: text('auth_provider').notNull(),
210
+ authSubject: text('auth_subject').notNull(),
211
+ email: text('email'),
212
+ displayName: text('display_name'),
213
+ createdAt: text('created_at').notNull(),
214
+ updatedAt: text('updated_at').notNull(),
215
+ },
216
+ (table) => ({
217
+ authSubjectUnique: uniqueIndex('control_auth_identities_subject_idx').on(
218
+ table.authProvider,
219
+ table.authSubject,
220
+ ),
221
+ }),
222
+ );
223
+
224
+ export const controlPasswordCredentials = sqliteTable(
225
+ 'control_password_credentials',
226
+ {
227
+ id: text('id').primaryKey(),
228
+ userId: text('user_id').notNull(),
229
+ email: text('email').notNull(),
230
+ passwordHash: text('password_hash').notNull(),
231
+ createdAt: text('created_at').notNull(),
232
+ updatedAt: text('updated_at').notNull(),
233
+ lastUsedAt: text('last_used_at'),
234
+ },
235
+ (table) => ({
236
+ emailUnique: uniqueIndex('control_password_credentials_email_idx').on(table.email),
237
+ userUnique: uniqueIndex('control_password_credentials_user_idx').on(table.userId),
238
+ }),
239
+ );
240
+
241
+ export const controlProjects = sqliteTable('control_projects', {
242
+ id: text('id').primaryKey(),
243
+ userId: text('user_id').notNull(),
244
+ name: text('name').notNull(),
245
+ slug: text('slug').notNull(),
246
+ status: text('status').notNull().default('active'),
247
+ createdAt: text('created_at').notNull(),
248
+ updatedAt: text('updated_at').notNull(),
249
+ });
250
+
251
+ export const controlSandboxes = sqliteTable('control_sandboxes', {
252
+ id: text('id').primaryKey(),
253
+ userId: text('user_id').notNull().unique(),
254
+ state: text('state').notNull(),
255
+ image: text('image').notNull(),
256
+ region: text('region').notNull(),
257
+ resourceProfile: text('resource_profile').notNull().default('standard'),
258
+ k8sNamespace: text('k8s_namespace'),
259
+ k8sPodName: text('k8s_pod_name'),
260
+ routerBaseUrl: text('router_base_url'),
261
+ workerServiceName: text('worker_service_name'),
262
+ s3Prefix: text('s3_prefix').notNull(),
263
+ gatewayKeyId: text('gateway_key_id'),
264
+ lastStartedAt: text('last_started_at'),
265
+ lastSeenAt: text('last_seen_at'),
266
+ idleTimeoutAt: text('idle_timeout_at'),
267
+ statusReason: text('status_reason'),
268
+ startupProgress: integer('startup_progress').notNull().default(0),
269
+ lastFailureCode: text('last_failure_code'),
270
+ lastFailureMessage: text('last_failure_message'),
271
+ createdAt: text('created_at').notNull(),
272
+ updatedAt: text('updated_at').notNull(),
273
+ });
274
+
275
+ export const controlWorkspaces = sqliteTable(
276
+ 'control_workspaces',
277
+ {
278
+ id: text('id').primaryKey(),
279
+ userId: text('user_id').notNull(),
280
+ projectId: text('project_id'),
281
+ sandboxId: text('sandbox_id').notNull(),
282
+ name: text('name').notNull(),
283
+ slug: text('slug').notNull(),
284
+ status: text('status').notNull().default('active'),
285
+ path: text('path').notNull(),
286
+ sourceType: text('source_type').notNull(),
287
+ gitUrl: text('git_url'),
288
+ defaultBranch: text('default_branch'),
289
+ createdAt: text('created_at').notNull(),
290
+ updatedAt: text('updated_at').notNull(),
291
+ },
292
+ (table) => ({
293
+ sandboxSlugUnique: uniqueIndex('control_workspaces_sandbox_slug_idx').on(
294
+ table.sandboxId,
295
+ table.slug,
296
+ ),
297
+ }),
298
+ );
299
+
300
+ export const controlSessions = sqliteTable('control_sessions', {
301
+ id: text('id').primaryKey(),
302
+ userId: text('user_id').notNull(),
303
+ sandboxId: text('sandbox_id').notNull(),
304
+ workspaceId: text('workspace_id').notNull(),
305
+ provider: text('provider').notNull(),
306
+ workerSessionId: text('worker_session_id'),
307
+ title: text('title').notNull(),
308
+ status: text('status').notNull(),
309
+ lastActivityAt: text('last_activity_at'),
310
+ createdAt: text('created_at').notNull(),
311
+ updatedAt: text('updated_at').notNull(),
312
+ });
313
+
314
+ export const controlGatewayUsers = sqliteTable(
315
+ 'control_gateway_users',
316
+ {
317
+ id: text('id').primaryKey(),
318
+ userId: text('user_id').notNull(),
319
+ provider: text('provider').notNull(),
320
+ externalUserId: text('external_user_id').notNull(),
321
+ createdAt: text('created_at').notNull(),
322
+ },
323
+ (table) => ({
324
+ userProviderUnique: uniqueIndex('control_gateway_users_user_provider_idx').on(
325
+ table.userId,
326
+ table.provider,
327
+ ),
328
+ providerExternalUnique: uniqueIndex('control_gateway_users_provider_external_idx').on(
329
+ table.provider,
330
+ table.externalUserId,
331
+ ),
332
+ }),
333
+ );
334
+
335
+ export const controlGatewayKeys = sqliteTable(
336
+ 'control_gateway_keys',
337
+ {
338
+ id: text('id').primaryKey(),
339
+ userId: text('user_id').notNull(),
340
+ sandboxId: text('sandbox_id').notNull(),
341
+ provider: text('provider').notNull(),
342
+ externalKeyId: text('external_key_id').notNull(),
343
+ keyCiphertext: text('key_ciphertext'),
344
+ status: text('status').notNull(),
345
+ createdAt: text('created_at').notNull(),
346
+ rotatedAt: text('rotated_at'),
347
+ revokedAt: text('revoked_at'),
348
+ },
349
+ (table) => ({
350
+ sandboxProviderUnique: uniqueIndex('control_gateway_keys_sandbox_provider_idx').on(
351
+ table.sandboxId,
352
+ table.provider,
353
+ ),
354
+ providerExternalUnique: uniqueIndex('control_gateway_keys_provider_external_idx').on(
355
+ table.provider,
356
+ table.externalKeyId,
357
+ ),
358
+ }),
359
+ );
360
+
361
+ export const controlHarnessUsers = sqliteTable(
362
+ 'control_harness_users',
363
+ {
364
+ id: text('id').primaryKey(),
365
+ userId: text('user_id').notNull(),
366
+ provider: text('provider').notNull(),
367
+ externalUserId: text('external_user_id').notNull(),
368
+ createdAt: text('created_at').notNull(),
369
+ },
370
+ (table) => ({
371
+ userProviderUnique: uniqueIndex('control_harness_users_user_provider_idx').on(
372
+ table.userId,
373
+ table.provider,
374
+ ),
375
+ providerExternalUnique: uniqueIndex('control_harness_users_provider_external_idx').on(
376
+ table.provider,
377
+ table.externalUserId,
378
+ ),
379
+ }),
380
+ );
381
+
382
+ export const controlHarnessKeys = sqliteTable(
383
+ 'control_harness_keys',
384
+ {
385
+ id: text('id').primaryKey(),
386
+ userId: text('user_id').notNull(),
387
+ sandboxId: text('sandbox_id').notNull(),
388
+ provider: text('provider').notNull(),
389
+ externalKeyId: text('external_key_id').notNull(),
390
+ keyCiphertext: text('key_ciphertext'),
391
+ secretName: text('secret_name'),
392
+ secretKey: text('secret_key'),
393
+ status: text('status').notNull(),
394
+ createdAt: text('created_at').notNull(),
395
+ rotatedAt: text('rotated_at'),
396
+ revokedAt: text('revoked_at'),
397
+ },
398
+ (table) => ({
399
+ sandboxProviderUnique: uniqueIndex('control_harness_keys_sandbox_provider_idx').on(
400
+ table.sandboxId,
401
+ table.provider,
402
+ ),
403
+ providerExternalUnique: uniqueIndex('control_harness_keys_provider_external_idx').on(
404
+ table.provider,
405
+ table.externalKeyId,
406
+ ),
407
+ }),
408
+ );
409
+
410
+ export const controlUsageEvents = sqliteTable('control_usage_events', {
411
+ id: text('id').primaryKey(),
412
+ userId: text('user_id').notNull(),
413
+ sandboxId: text('sandbox_id').notNull(),
414
+ workspaceId: text('workspace_id'),
415
+ sessionId: text('session_id'),
416
+ gatewayKeyId: text('gateway_key_id'),
417
+ provider: text('provider').notNull(),
418
+ model: text('model').notNull(),
419
+ inputTokens: integer('input_tokens').notNull().default(0),
420
+ outputTokens: integer('output_tokens').notNull().default(0),
421
+ cachedTokens: integer('cached_tokens').notNull().default(0),
422
+ costUsd: real('cost_usd').notNull().default(0),
423
+ externalRequestId: text('external_request_id'),
424
+ occurredAt: text('occurred_at').notNull(),
425
+ importedAt: text('imported_at').notNull(),
426
+ });
427
+
428
+ export const controlHarnessUsageEvents = sqliteTable(
429
+ 'control_harness_usage_events',
430
+ {
431
+ id: text('id').primaryKey(),
432
+ userId: text('user_id').notNull(),
433
+ sandboxId: text('sandbox_id').notNull(),
434
+ workspaceId: text('workspace_id'),
435
+ sessionId: text('session_id'),
436
+ provider: text('provider').notNull(),
437
+ module: text('module').notNull(),
438
+ tool: text('tool'),
439
+ runId: text('run_id'),
440
+ jobId: text('job_id'),
441
+ externalEventId: text('external_event_id'),
442
+ computeUnits: real('compute_units').notNull().default(0),
443
+ costUsd: real('cost_usd').notNull().default(0),
444
+ status: text('status').notNull().default('unknown'),
445
+ metadataJson: text('metadata_json').notNull().default('{}'),
446
+ occurredAt: text('occurred_at').notNull(),
447
+ importedAt: text('imported_at').notNull(),
448
+ },
449
+ (table) => ({
450
+ providerExternalEventUnique: uniqueIndex('control_harness_usage_provider_event_idx')
451
+ .on(table.provider, table.externalEventId),
452
+ }),
453
+ );
454
+
455
+ export const controlUsageImportState = sqliteTable('control_usage_import_state', {
456
+ id: text('id').primaryKey(),
457
+ provider: text('provider').notNull(),
458
+ source: text('source').notNull(),
459
+ cursor: text('cursor'),
460
+ lastStartedAt: text('last_started_at'),
461
+ lastSucceededAt: text('last_succeeded_at'),
462
+ lastFailedAt: text('last_failed_at'),
463
+ lastFailureMessage: text('last_failure_message'),
464
+ lastSourceCount: integer('last_source_count').notNull().default(0),
465
+ lastImportedCount: integer('last_imported_count').notNull().default(0),
466
+ lastDuplicateCount: integer('last_duplicate_count').notNull().default(0),
467
+ lastFailureCount: integer('last_failure_count').notNull().default(0),
468
+ updatedAt: text('updated_at').notNull(),
469
+ }, (table) => ({
470
+ providerSourceIdx: uniqueIndex('control_usage_import_state_provider_source_idx')
471
+ .on(table.provider, table.source),
472
+ }));
473
+
474
+ export const controlAuditLogs = sqliteTable('control_audit_logs', {
475
+ id: text('id').primaryKey(),
476
+ userId: text('user_id'),
477
+ action: text('action').notNull(),
478
+ resourceType: text('resource_type').notNull(),
479
+ resourceId: text('resource_id'),
480
+ metadataJson: text('metadata_json').notNull(),
481
+ createdAt: text('created_at').notNull(),
482
+ });
@@ -21,6 +21,12 @@ export type ApiErrorCode =
21
21
  | 'conflict'
22
22
  | 'provider_goal_error'
23
23
  | 'forbidden'
24
+ | 'unauthorized'
25
+ | 'invalid_route_token'
26
+ | 'gateway_unavailable'
27
+ | 'account_inactive'
28
+ | 'quota_exceeded'
29
+ | 'harness_unavailable'
24
30
  | 'goal_feature_disabled'
25
31
  | 'internal_error'
26
32
  | 'service_unavailable';
@@ -444,6 +450,30 @@ export interface WorkspaceTreeDto {
444
450
  nodes: WorkspaceTreeNodeDto[];
445
451
  }
446
452
 
453
+ export interface WorkspaceFileDto {
454
+ path: string;
455
+ absPath: string;
456
+ kind: 'file' | 'directory';
457
+ size: number;
458
+ updatedAt: string;
459
+ }
460
+
461
+ export interface WriteWorkspaceFileInput {
462
+ path: string;
463
+ content: string;
464
+ }
465
+
466
+ export interface MoveWorkspaceFileInput {
467
+ fromPath: string;
468
+ toPath: string;
469
+ overwrite?: boolean;
470
+ }
471
+
472
+ export interface DeleteWorkspaceFileInput {
473
+ path: string;
474
+ recursive?: boolean;
475
+ }
476
+
447
477
  export interface ThreadWorkspaceTreeNodeDto {
448
478
  name: string;
449
479
  path: string;
@@ -662,6 +692,7 @@ export interface UpdatePluginInput {
662
692
  export interface ImportPluginInput {
663
693
  manifest?: unknown;
664
694
  manifestJson?: string;
695
+ manifestUrl?: string;
665
696
  enabled?: boolean;
666
697
  }
667
698
 
@@ -1072,6 +1103,7 @@ export interface CreateThreadInput {
1072
1103
  title?: string;
1073
1104
  provider?: AgentBackendIdDto;
1074
1105
  model: string;
1106
+ reasoningEffort?: ReasoningEffortDto | null;
1075
1107
  approvalMode: ApprovalMode;
1076
1108
  }
1077
1109
 
@@ -0,0 +1,137 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+
3
+ function base64UrlEncode(value: Buffer | string): string {
4
+ return Buffer.from(value)
5
+ .toString('base64')
6
+ .replaceAll('+', '-')
7
+ .replaceAll('/', '_')
8
+ .replaceAll('=', '');
9
+ }
10
+
11
+ function base64UrlDecode(value: string): Buffer {
12
+ const normalized = value.replaceAll('-', '+').replaceAll('_', '/');
13
+ const padding = '='.repeat((4 - (normalized.length % 4)) % 4);
14
+ return Buffer.from(`${normalized}${padding}`, 'base64');
15
+ }
16
+
17
+ function sign(input: string, secret: string): string {
18
+ return base64UrlEncode(createHmac('sha256', secret).update(input).digest());
19
+ }
20
+
21
+ export interface RouteTokenPayload extends SignedTokenPayload {
22
+ sandbox_id: string;
23
+ project_id?: string;
24
+ workspace_id?: string;
25
+ session_id?: string;
26
+ scopes: string[];
27
+ iat: number;
28
+ jti: string;
29
+ }
30
+
31
+ export interface SignedTokenPayload {
32
+ sub: string;
33
+ iat?: number;
34
+ exp: number;
35
+ jti?: string;
36
+ [key: string]: unknown;
37
+ }
38
+
39
+ export interface SigningKey {
40
+ id: string;
41
+ secret: string;
42
+ }
43
+
44
+ export function createSignedToken(
45
+ payload: SignedTokenPayload,
46
+ secret: string,
47
+ options: { kid?: string } = {},
48
+ ): string {
49
+ const header = {
50
+ alg: 'HS256',
51
+ typ: 'JWT',
52
+ ...(options.kid ? { kid: options.kid } : {}),
53
+ };
54
+ const encodedHeader = base64UrlEncode(JSON.stringify(header));
55
+ const encodedPayload = base64UrlEncode(JSON.stringify(payload));
56
+ const signingInput = `${encodedHeader}.${encodedPayload}`;
57
+ return `${signingInput}.${sign(signingInput, secret)}`;
58
+ }
59
+
60
+ function decodeHeader(token: string) {
61
+ const [encodedHeader] = token.split('.');
62
+ if (!encodedHeader) {
63
+ throw new Error('Invalid token shape.');
64
+ }
65
+ return JSON.parse(base64UrlDecode(encodedHeader).toString('utf8')) as {
66
+ alg?: string;
67
+ typ?: string;
68
+ kid?: string;
69
+ };
70
+ }
71
+
72
+ export function verifySignedToken<
73
+ TPayload extends { sub: string; exp: number } = RouteTokenPayload,
74
+ >(
75
+ token: string,
76
+ secret: string,
77
+ nowSeconds = Math.floor(Date.now() / 1000),
78
+ ) {
79
+ const parts = token.split('.');
80
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {
81
+ throw new Error('Invalid token shape.');
82
+ }
83
+
84
+ const signingInput = `${parts[0]}.${parts[1]}`;
85
+ const expected = sign(signingInput, secret);
86
+ const actualBuffer = Buffer.from(parts[2]);
87
+ const expectedBuffer = Buffer.from(expected);
88
+ if (
89
+ actualBuffer.length !== expectedBuffer.length ||
90
+ !timingSafeEqual(actualBuffer, expectedBuffer)
91
+ ) {
92
+ throw new Error('Invalid token signature.');
93
+ }
94
+
95
+ const payload = JSON.parse(base64UrlDecode(parts[1]).toString('utf8')) as TPayload;
96
+ if (payload.exp <= nowSeconds) {
97
+ throw new Error('Token expired.');
98
+ }
99
+
100
+ return payload;
101
+ }
102
+
103
+ export function verifySignedTokenWithKeys<
104
+ TPayload extends { sub: string; exp: number } = RouteTokenPayload,
105
+ >(
106
+ token: string,
107
+ keys: SigningKey[],
108
+ nowSeconds = Math.floor(Date.now() / 1000),
109
+ ) {
110
+ if (keys.length === 0) {
111
+ throw new Error('No signing keys configured.');
112
+ }
113
+
114
+ const header = decodeHeader(token);
115
+ if (header.alg !== 'HS256') {
116
+ throw new Error('Unsupported token algorithm.');
117
+ }
118
+
119
+ if (header.kid) {
120
+ const key = keys.find((candidate) => candidate.id === header.kid);
121
+ if (!key) {
122
+ throw new Error('Unknown token key id.');
123
+ }
124
+ return verifySignedToken<TPayload>(token, key.secret, nowSeconds);
125
+ }
126
+
127
+ let lastError: unknown = null;
128
+ for (const key of keys) {
129
+ try {
130
+ return verifySignedToken<TPayload>(token, key.secret, nowSeconds);
131
+ } catch (error) {
132
+ lastError = error;
133
+ }
134
+ }
135
+
136
+ throw lastError instanceof Error ? lastError : new Error('Invalid token.');
137
+ }