@kaeawc/auto-mobile 0.0.85 → 0.0.87

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 (87) hide show
  1. package/README.md +3 -3
  2. package/README.md.backup +3 -3
  3. package/dist/build-info.json +10 -10
  4. package/dist/src/index.js +1173 -840
  5. package/dist/src/utils/workers/iosBundleExtractWorker.js +3 -3
  6. package/node_modules/strtok3/lib/stream/StreamReader.js +6 -1
  7. package/node_modules/strtok3/package.json +1 -1
  8. package/package.json +21 -17
  9. package/dist/schemas/managed-slot-contract.json +0 -142
  10. package/dist/src/db/eventTables.ts +0 -8
  11. package/dist/src/db/migrations/2025_12_28_000_initial_schema.ts +0 -28
  12. package/dist/src/db/migrations/2025_12_30_000_performance_thresholds.ts +0 -85
  13. package/dist/src/db/migrations/2025_12_30_001_navigation_graph.ts +0 -223
  14. package/dist/src/db/migrations/2025_12_31_000_accessibility_baselines.ts +0 -34
  15. package/dist/src/db/migrations/2025_12_31_001_memory_audit.ts +0 -120
  16. package/dist/src/db/migrations/2026_01_01_000_recomposition_metrics.ts +0 -44
  17. package/dist/src/db/migrations/2026_01_02_000_prediction_history.ts +0 -87
  18. package/dist/src/db/migrations/2026_01_03_000_feature_flags.ts +0 -17
  19. package/dist/src/db/migrations/2026_01_03_000_test_executions.ts +0 -45
  20. package/dist/src/db/migrations/2026_01_05_000_tool_calls.ts +0 -31
  21. package/dist/src/db/migrations/2026_01_08_000_test_coverage.ts +0 -109
  22. package/dist/src/db/migrations/2026_01_09_000_video_recordings.ts +0 -66
  23. package/dist/src/db/migrations/2026_01_10_000_device_snapshots.ts +0 -54
  24. package/dist/src/db/migrations/2026_01_11_000_installed_apps.ts +0 -33
  25. package/dist/src/db/migrations/2026_01_11_000_video_recording_highlights.ts +0 -24
  26. package/dist/src/db/migrations/2026_01_13_000_installed_apps_session_tracking.ts +0 -27
  27. package/dist/src/db/migrations/2026_01_14_000_appearance_config.ts +0 -16
  28. package/dist/src/db/migrations/2026_01_25_000_navigation_screenshots.ts +0 -27
  29. package/dist/src/db/migrations/2026_01_25_001_test_run_details.ts +0 -97
  30. package/dist/src/db/migrations/2026_01_27_000_crash_anr_monitoring.ts +0 -204
  31. package/dist/src/db/migrations/2026_01_27_000_failures.ts +0 -217
  32. package/dist/src/db/migrations/2026_01_29_000_named_nodes.ts +0 -88
  33. package/dist/src/db/migrations/2026_01_30_000_performance_live_metrics.ts +0 -108
  34. package/dist/src/db/migrations/2026_03_15_000_telemetry_events.ts +0 -184
  35. package/dist/src/db/migrations/2026_03_18_000_navigation_events.ts +0 -36
  36. package/dist/src/db/migrations/2026_03_19_000_storage_events.ts +0 -37
  37. package/dist/src/db/migrations/2026_03_19_001_layout_events.ts +0 -39
  38. package/dist/src/db/migrations/2026_03_19_002_storage_events_previous_value.ts +0 -23
  39. package/dist/src/db/migrations/2026_03_19_003_layout_events_screen_name.ts +0 -23
  40. package/dist/src/db/migrations/2026_03_20_000_network_event_details.ts +0 -32
  41. package/dist/src/db/migrations/2026_04_01_000_drop_custom_events.ts +0 -41
  42. package/dist/src/db/migrations/2026_04_02_000_device_sessions.ts +0 -51
  43. package/dist/src/db/migrations/2026_07_01_000_failure_groups_signature_unique.ts +0 -162
  44. package/dist/src/db/migrations/2026_07_02_000_event_composite_indexes.ts +0 -62
  45. package/dist/src/db/migrations/2026_07_03_000_drop_redundant_device_indexes.ts +0 -66
  46. package/dist/src/db/migrations/2026_07_03_000_repair_datetime_now_defaults.ts +0 -278
  47. package/dist/src/db/migrations/2026_07_04_000_storage_events_key_lookup.ts +0 -60
  48. package/dist/src/db/migrations/2026_07_05_000_repair_updated_at_defaults.ts +0 -239
  49. package/dist/src/db/migrations/2026_07_24_000_device_locks.ts +0 -27
  50. package/dist/src/db/migrations/2026_07_27_000_session_tool_capabilities.ts +0 -17
  51. package/dist/src/db/migrations/2026_07_31_000_video_recordings_owner_session.ts +0 -39
  52. package/dist/src/db/migrations/2026_08_02_000_navigation_provenance.ts +0 -159
  53. package/dist/src/db/migrations/2026_08_17_000_emulator_loss_incidents.ts +0 -39
  54. package/dist/src/db/migrations/2026_08_22_000_provision_device_operations.ts +0 -21
  55. package/dist/src/db/migrations/2026_08_22_001_session_tool_overrides.ts +0 -106
  56. package/dist/src/db/migrations/2026_08_22_002_navigation_observation_eviction_index.ts +0 -67
  57. package/dist/src/db/migrations/2026_08_22_003_device_teardown_operations.ts +0 -19
  58. package/dist/src/db/migrations/2026_08_23_000_device_teardown_operation_owner.ts +0 -19
  59. package/dist/src/db/migrations/2026_08_23_001_default_existing_delete_device_disabled.ts +0 -26
  60. package/dist/src/db/migrations/2026_08_24_000_installed_apps_profile_type.ts +0 -15
  61. package/dist/src/db/migrations/2026_09_06_000_tool_selection_profile_provenance.ts +0 -36
  62. package/dist/src/db/migrations/2026_09_08_000_provision_device_operations_expiry.ts +0 -25
  63. package/dist/src/db/migrations/2026_09_10_000_provision_device_operation_attempt.ts +0 -25
  64. package/dist/src/db/migrations/2026_09_10_001_device_session_retention_index.ts +0 -14
  65. package/dist/src/db/migrations/2026_09_13_000_device_snapshot_vm_reclaim.ts +0 -63
  66. package/dist/src/db/migrations/2026_09_14_000_device_session_stable_identity.ts +0 -9
  67. package/dist/src/db/migrations/2026_09_14_001_device_session_identity_writer_fence.ts +0 -45
  68. package/dist/src/db/migrations/2026_09_16_000_device_session_liveness_owner.ts +0 -9
  69. package/dist/src/db/migrations/2026_09_16_001_device_session_liveness_contract.ts +0 -37
  70. package/dist/src/db/migrations/2026_09_17_000_device_session_liveness_writer_fence.ts +0 -48
  71. package/dist/src/db/migrations/2026_09_17_001_device_session_identity_fence_same_serial.ts +0 -53
  72. package/dist/src/db/migrations/2026_09_20_000_provision_device_lifecycle_outcome.ts +0 -32
  73. package/dist/src/db/migrations/2026_09_29_000_navigation_suggestion_provenance.ts +0 -41
  74. package/dist/src/db/migrations/2026_09_30_000_video_recording_display.ts +0 -17
  75. package/dist/src/db/migrations/2026_09_30_001_network_capture_fields.ts +0 -30
  76. package/dist/src/db/migrations/2026_10_05_000_device_locks_device_identity.ts +0 -28
  77. package/dist/src/db/migrations/2026_10_05_001_storage_events_key_lookup_insertion_order.ts +0 -38
  78. package/dist/src/db/migrations/2026_10_05_002_test_edge_coverage_edge_id_index.ts +0 -19
  79. package/dist/src/db/migrations/2026_10_09_000_device_resource_applications.ts +0 -22
  80. package/dist/src/db/migrations/2026_10_09_001_drop_provision_device_operations.ts +0 -32
  81. package/dist/src/db/migrations/2026_10_09_002_drop_device_teardown_operations.ts +0 -28
  82. package/dist/src/db/migrations/2026_10_12_000_rename_recording_tool_names.ts +0 -44
  83. package/dist/src/db/migrations/2026_10_12_001_video_recording_capture_teardown.ts +0 -17
  84. package/dist/src/db/migrations/2026_10_13_000_video_recording_recorder_daemon.ts +0 -17
  85. package/dist/src/index.js.map +0 -1
  86. package/dist/src/utils/workers/iosBundleExtractWorker.js.map +0 -1
  87. package/schemas/managed-slot-contract.json +0 -142
@@ -1,109 +0,0 @@
1
- import { type Kysely, sql } from "kysely";
2
-
3
- export async function up(db: Kysely<unknown>): Promise<void> {
4
- // Create test_coverage_sessions table to track test run sessions
5
- await db.schema
6
- .createTable("test_coverage_sessions")
7
- .ifNotExists()
8
- .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
9
- .addColumn("session_uuid", "text", (col) => col.notNull().unique())
10
- .addColumn("app_id", "text", (col) =>
11
- col.notNull().references("navigation_apps.app_id").onDelete("cascade"),
12
- )
13
- .addColumn("start_time", "integer", (col) => col.notNull())
14
- .addColumn("end_time", "integer")
15
- .addColumn("total_nodes_visited", "integer", (col) => col.notNull().defaultTo(0))
16
- .addColumn("total_edges_traversed", "integer", (col) => col.notNull().defaultTo(0))
17
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
18
- .execute();
19
-
20
- // Create index on session_uuid for fast lookups
21
- await db.schema
22
- .createIndex("idx_test_coverage_sessions_uuid")
23
- .ifNotExists()
24
- .on("test_coverage_sessions")
25
- .column("session_uuid")
26
- .execute();
27
-
28
- // Create index on app_id for filtering by app
29
- await db.schema
30
- .createIndex("idx_test_coverage_sessions_app")
31
- .ifNotExists()
32
- .on("test_coverage_sessions")
33
- .column("app_id")
34
- .execute();
35
-
36
- // Create test_node_coverage table to track which nodes were visited during tests
37
- await db.schema
38
- .createTable("test_node_coverage")
39
- .ifNotExists()
40
- .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
41
- .addColumn("session_id", "integer", (col) =>
42
- col.notNull().references("test_coverage_sessions.id").onDelete("cascade"),
43
- )
44
- .addColumn("node_id", "integer", (col) =>
45
- col.notNull().references("navigation_nodes.id").onDelete("cascade"),
46
- )
47
- .addColumn("visit_count", "integer", (col) => col.notNull().defaultTo(1))
48
- .addColumn("first_visit_time", "integer", (col) => col.notNull())
49
- .addColumn("last_visit_time", "integer", (col) => col.notNull())
50
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
51
- .execute();
52
-
53
- // Create unique constraint on (session_id, node_id)
54
- await db.schema
55
- .createIndex("idx_test_node_coverage_session_node")
56
- .ifNotExists()
57
- .on("test_node_coverage")
58
- .columns(["session_id", "node_id"])
59
- .unique()
60
- .execute();
61
-
62
- // Create index on session_id for efficient queries
63
- await db.schema
64
- .createIndex("idx_test_node_coverage_session")
65
- .ifNotExists()
66
- .on("test_node_coverage")
67
- .column("session_id")
68
- .execute();
69
-
70
- // Create test_edge_coverage table to track which edges were traversed during tests
71
- await db.schema
72
- .createTable("test_edge_coverage")
73
- .ifNotExists()
74
- .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
75
- .addColumn("session_id", "integer", (col) =>
76
- col.notNull().references("test_coverage_sessions.id").onDelete("cascade"),
77
- )
78
- .addColumn("edge_id", "integer", (col) =>
79
- col.notNull().references("navigation_edges.id").onDelete("cascade"),
80
- )
81
- .addColumn("traversal_count", "integer", (col) => col.notNull().defaultTo(1))
82
- .addColumn("first_traversal_time", "integer", (col) => col.notNull())
83
- .addColumn("last_traversal_time", "integer", (col) => col.notNull())
84
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
85
- .execute();
86
-
87
- // Create unique constraint on (session_id, edge_id)
88
- await db.schema
89
- .createIndex("idx_test_edge_coverage_session_edge")
90
- .ifNotExists()
91
- .on("test_edge_coverage")
92
- .columns(["session_id", "edge_id"])
93
- .unique()
94
- .execute();
95
-
96
- // Create index on session_id for efficient queries
97
- await db.schema
98
- .createIndex("idx_test_edge_coverage_session")
99
- .ifNotExists()
100
- .on("test_edge_coverage")
101
- .column("session_id")
102
- .execute();
103
- }
104
-
105
- export async function down(db: Kysely<unknown>): Promise<void> {
106
- await db.schema.dropTable("test_edge_coverage").execute();
107
- await db.schema.dropTable("test_node_coverage").execute();
108
- await db.schema.dropTable("test_coverage_sessions").execute();
109
- }
@@ -1,66 +0,0 @@
1
- import { type Kysely, sql } from "kysely";
2
-
3
- export async function up(db: Kysely<unknown>): Promise<void> {
4
- await db.schema
5
- .createTable("video_recordings")
6
- .ifNotExists()
7
- .addColumn("recording_id", "text", (col) => col.primaryKey())
8
- .addColumn("device_id", "text", (col) => col.notNull())
9
- .addColumn("platform", "text", (col) => col.notNull())
10
- .addColumn("status", "text", (col) => col.notNull())
11
- .addColumn("output_name", "text")
12
- .addColumn("file_name", "text", (col) => col.notNull())
13
- .addColumn("file_path", "text", (col) => col.notNull())
14
- .addColumn("format", "text", (col) => col.notNull())
15
- .addColumn("size_bytes", "integer", (col) => col.notNull().defaultTo(0))
16
- .addColumn("duration_ms", "integer")
17
- .addColumn("codec", "text")
18
- .addColumn("created_at", "text", (col) => col.notNull())
19
- .addColumn("started_at", "text", (col) => col.notNull())
20
- .addColumn("ended_at", "text")
21
- .addColumn("last_accessed_at", "text", (col) => col.notNull())
22
- .addColumn("config_json", "text", (col) => col.notNull())
23
- .execute();
24
-
25
- await db.schema
26
- .createIndex("idx_video_recordings_status")
27
- .ifNotExists()
28
- .on("video_recordings")
29
- .column("status")
30
- .execute();
31
-
32
- await db.schema
33
- .createIndex("idx_video_recordings_device_status")
34
- .ifNotExists()
35
- .on("video_recordings")
36
- .columns(["device_id", "status"])
37
- .execute();
38
-
39
- await db.schema
40
- .createIndex("idx_video_recordings_last_accessed_at")
41
- .ifNotExists()
42
- .on("video_recordings")
43
- .column("last_accessed_at")
44
- .execute();
45
-
46
- await db.schema
47
- .createIndex("idx_video_recordings_created_at")
48
- .ifNotExists()
49
- .on("video_recordings")
50
- .column("created_at")
51
- .execute();
52
-
53
- await db.schema
54
- .createTable("video_recording_configs")
55
- .ifNotExists()
56
- .addColumn("key", "text", (col) => col.primaryKey())
57
- .addColumn("config_json", "text", (col) => col.notNull())
58
- .addColumn("updated_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
59
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
60
- .execute();
61
- }
62
-
63
- export async function down(db: Kysely<unknown>): Promise<void> {
64
- await db.schema.dropTable("video_recordings").execute();
65
- await db.schema.dropTable("video_recording_configs").execute();
66
- }
@@ -1,54 +0,0 @@
1
- import { type Kysely, sql } from "kysely";
2
-
3
- export async function up(db: Kysely<unknown>): Promise<void> {
4
- await db.schema
5
- .createTable("device_snapshots")
6
- .ifNotExists()
7
- .addColumn("snapshot_name", "text", (col) => col.primaryKey())
8
- .addColumn("device_id", "text", (col) => col.notNull())
9
- .addColumn("device_name", "text", (col) => col.notNull())
10
- .addColumn("platform", "text", (col) => col.notNull())
11
- .addColumn("snapshot_type", "text", (col) => col.notNull())
12
- .addColumn("include_app_data", "integer", (col) => col.notNull())
13
- .addColumn("include_settings", "integer", (col) => col.notNull())
14
- .addColumn("created_at", "text", (col) => col.notNull())
15
- .addColumn("last_accessed_at", "text", (col) => col.notNull())
16
- .addColumn("size_bytes", "integer", (col) => col.notNull().defaultTo(0))
17
- .addColumn("manifest_json", "text", (col) => col.notNull())
18
- .execute();
19
-
20
- await db.schema
21
- .createIndex("idx_device_snapshots_device_id")
22
- .ifNotExists()
23
- .on("device_snapshots")
24
- .column("device_id")
25
- .execute();
26
-
27
- await db.schema
28
- .createIndex("idx_device_snapshots_last_accessed_at")
29
- .ifNotExists()
30
- .on("device_snapshots")
31
- .column("last_accessed_at")
32
- .execute();
33
-
34
- await db.schema
35
- .createIndex("idx_device_snapshots_created_at")
36
- .ifNotExists()
37
- .on("device_snapshots")
38
- .column("created_at")
39
- .execute();
40
-
41
- await db.schema
42
- .createTable("device_snapshot_configs")
43
- .ifNotExists()
44
- .addColumn("key", "text", (col) => col.primaryKey())
45
- .addColumn("config_json", "text", (col) => col.notNull())
46
- .addColumn("updated_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
47
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
48
- .execute();
49
- }
50
-
51
- export async function down(db: Kysely<unknown>): Promise<void> {
52
- await db.schema.dropTable("device_snapshots").execute();
53
- await db.schema.dropTable("device_snapshot_configs").execute();
54
- }
@@ -1,33 +0,0 @@
1
- import type { Kysely } from "kysely";
2
-
3
- export async function up(db: Kysely<unknown>): Promise<void> {
4
- await db.schema
5
- .createTable("installed_apps")
6
- .ifNotExists()
7
- .addColumn("device_id", "text", (col) => col.notNull())
8
- .addColumn("user_id", "integer", (col) => col.notNull())
9
- .addColumn("package_name", "text", (col) => col.notNull())
10
- .addColumn("is_system", "integer", (col) => col.notNull().defaultTo(0))
11
- .addColumn("installed_at", "integer", (col) => col.notNull())
12
- .addColumn("last_verified_at", "integer", (col) => col.notNull())
13
- .addPrimaryKeyConstraint("installed_apps_pk", ["device_id", "user_id", "package_name"])
14
- .execute();
15
-
16
- await db.schema
17
- .createIndex("idx_installed_apps_lookup")
18
- .ifNotExists()
19
- .on("installed_apps")
20
- .columns(["device_id", "package_name"])
21
- .execute();
22
-
23
- await db.schema
24
- .createIndex("idx_installed_apps_last_verified")
25
- .ifNotExists()
26
- .on("installed_apps")
27
- .columns(["device_id", "last_verified_at"])
28
- .execute();
29
- }
30
-
31
- export async function down(db: Kysely<unknown>): Promise<void> {
32
- await db.schema.dropTable("installed_apps").execute();
33
- }
@@ -1,24 +0,0 @@
1
- import type { Kysely } from "kysely";
2
- import { sql } from "kysely";
3
-
4
- async function columnExists(
5
- db: Kysely<unknown>,
6
- tableName: string,
7
- columnName: string,
8
- ): Promise<boolean> {
9
- const result = await sql<{ name: string }>`
10
- SELECT name FROM pragma_table_info(${tableName}) WHERE name = ${columnName}
11
- `.execute(db);
12
- return result.rows.length > 0;
13
- }
14
-
15
- export async function up(db: Kysely<unknown>): Promise<void> {
16
- const exists = await columnExists(db, "video_recordings", "highlights_json");
17
- if (!exists) {
18
- await db.schema.alterTable("video_recordings").addColumn("highlights_json", "text").execute();
19
- }
20
- }
21
-
22
- export async function down(db: Kysely<unknown>): Promise<void> {
23
- await db.schema.alterTable("video_recordings").dropColumn("highlights_json").execute();
24
- }
@@ -1,27 +0,0 @@
1
- import type { Kysely } from "kysely";
2
-
3
- export async function up(db: Kysely<unknown>): Promise<void> {
4
- // Add session tracking columns to installed_apps table
5
- await db.schema.alterTable("installed_apps").addColumn("daemon_session_id", "text").execute();
6
-
7
- await db.schema
8
- .alterTable("installed_apps")
9
- .addColumn("device_session_start", "integer")
10
- .execute();
11
-
12
- // Create index for efficient session-based cleanup queries
13
- await db.schema
14
- .createIndex("idx_installed_apps_session")
15
- .ifNotExists()
16
- .on("installed_apps")
17
- .columns(["daemon_session_id", "device_session_start"])
18
- .execute();
19
- }
20
-
21
- export async function down(db: Kysely<unknown>): Promise<void> {
22
- await db.schema.dropIndex("idx_installed_apps_session").execute();
23
-
24
- await db.schema.alterTable("installed_apps").dropColumn("daemon_session_id").execute();
25
-
26
- await db.schema.alterTable("installed_apps").dropColumn("device_session_start").execute();
27
- }
@@ -1,16 +0,0 @@
1
- import { type Kysely, sql } from "kysely";
2
-
3
- export async function up(db: Kysely<unknown>): Promise<void> {
4
- await db.schema
5
- .createTable("appearance_configs")
6
- .ifNotExists()
7
- .addColumn("key", "text", (col) => col.primaryKey())
8
- .addColumn("config_json", "text", (col) => col.notNull())
9
- .addColumn("updated_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
10
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
11
- .execute();
12
- }
13
-
14
- export async function down(db: Kysely<unknown>): Promise<void> {
15
- await db.schema.dropTable("appearance_configs").execute();
16
- }
@@ -1,27 +0,0 @@
1
- import type { Kysely } from "kysely";
2
- import { sql } from "kysely";
3
-
4
- async function columnExists(
5
- db: Kysely<unknown>,
6
- tableName: string,
7
- columnName: string,
8
- ): Promise<boolean> {
9
- const result = await sql<{ name: string }>`
10
- SELECT name FROM pragma_table_info(${tableName}) WHERE name = ${columnName}
11
- `.execute(db);
12
- return result.rows.length > 0;
13
- }
14
-
15
- export async function up(db: Kysely<unknown>): Promise<void> {
16
- // Add screenshot_path column to navigation_nodes table (if not already present)
17
- const hasColumn = await columnExists(db, "navigation_nodes", "screenshot_path");
18
- if (!hasColumn) {
19
- await db.schema.alterTable("navigation_nodes").addColumn("screenshot_path", "text").execute();
20
- }
21
- }
22
-
23
- export async function down(db: Kysely<unknown>): Promise<void> {
24
- // SQLite doesn't support dropping columns directly, so we need to recreate the table
25
- // For simplicity, we'll use a workaround since SQLite 3.35.0+ supports DROP COLUMN
26
- await db.schema.alterTable("navigation_nodes").dropColumn("screenshot_path").execute();
27
- }
@@ -1,97 +0,0 @@
1
- import type { Kysely } from "kysely";
2
- import { sql } from "kysely";
3
-
4
- async function columnExists(
5
- db: Kysely<unknown>,
6
- tableName: string,
7
- columnName: string,
8
- ): Promise<boolean> {
9
- const result = await sql<{ name: string }>`
10
- SELECT name FROM pragma_table_info(${tableName}) WHERE name = ${columnName}
11
- `.execute(db);
12
- return result.rows.length > 0;
13
- }
14
-
15
- async function tableExists(db: Kysely<unknown>, tableName: string): Promise<boolean> {
16
- const result = await sql<{ name: string }>`
17
- SELECT name FROM sqlite_master WHERE type = 'table' AND name = ${tableName}
18
- `.execute(db);
19
- return result.rows.length > 0;
20
- }
21
-
22
- export async function up(db: Kysely<unknown>): Promise<void> {
23
- // Add error_message column to test_executions for storing failure details
24
- if (!(await columnExists(db, "test_executions", "error_message"))) {
25
- await db.schema.alterTable("test_executions").addColumn("error_message", "text").execute();
26
- }
27
-
28
- // Add video_path for test recordings
29
- if (!(await columnExists(db, "test_executions", "video_path"))) {
30
- await db.schema.alterTable("test_executions").addColumn("video_path", "text").execute();
31
- }
32
-
33
- // Add snapshot_path for test snapshots
34
- if (!(await columnExists(db, "test_executions", "snapshot_path"))) {
35
- await db.schema.alterTable("test_executions").addColumn("snapshot_path", "text").execute();
36
- }
37
-
38
- // Create test_execution_steps table for step-level data (if not exists)
39
- if (!(await tableExists(db, "test_execution_steps"))) {
40
- await db.schema
41
- .createTable("test_execution_steps")
42
- .ifNotExists()
43
- .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
44
- .addColumn("execution_id", "integer", (col) =>
45
- col.notNull().references("test_executions.id").onDelete("cascade"),
46
- )
47
- .addColumn("step_index", "integer", (col) => col.notNull())
48
- .addColumn("action", "text", (col) => col.notNull()) // Tool name or action type
49
- .addColumn("target", "text") // Element target description
50
- .addColumn("status", "text", (col) => col.notNull()) // completed, failed, skipped
51
- .addColumn("duration_ms", "integer", (col) => col.notNull())
52
- .addColumn("screen_name", "text") // Screen name when step was executed
53
- .addColumn("screenshot_path", "text") // Screenshot captured for this step
54
- .addColumn("error_message", "text") // Error message if step failed
55
- .addColumn("details_json", "text") // Additional step details as JSON
56
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
57
- .execute();
58
-
59
- await db.schema
60
- .createIndex("idx_test_execution_steps_execution")
61
- .ifNotExists()
62
- .on("test_execution_steps")
63
- .column("execution_id")
64
- .execute();
65
- }
66
-
67
- // Create test_execution_screens table for screens visited during test (if not exists)
68
- if (!(await tableExists(db, "test_execution_screens"))) {
69
- await db.schema
70
- .createTable("test_execution_screens")
71
- .ifNotExists()
72
- .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
73
- .addColumn("execution_id", "integer", (col) =>
74
- col.notNull().references("test_executions.id").onDelete("cascade"),
75
- )
76
- .addColumn("screen_name", "text", (col) => col.notNull())
77
- .addColumn("visit_order", "integer", (col) => col.notNull())
78
- .addColumn("timestamp", "integer", (col) => col.notNull())
79
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
80
- .execute();
81
-
82
- await db.schema
83
- .createIndex("idx_test_execution_screens_execution")
84
- .ifNotExists()
85
- .on("test_execution_screens")
86
- .column("execution_id")
87
- .execute();
88
- }
89
- }
90
-
91
- export async function down(db: Kysely<unknown>): Promise<void> {
92
- await db.schema.dropTable("test_execution_screens").execute();
93
- await db.schema.dropTable("test_execution_steps").execute();
94
-
95
- // Note: SQLite doesn't support DROP COLUMN, so we can't remove the added columns
96
- // In production, would need to recreate the table without these columns
97
- }
@@ -1,204 +0,0 @@
1
- import type { Kysely } from "kysely";
2
- import { sql } from "kysely";
3
-
4
- async function columnExists(
5
- db: Kysely<unknown>,
6
- tableName: string,
7
- columnName: string,
8
- ): Promise<boolean> {
9
- const result = await sql<{ name: string }>`
10
- SELECT name FROM pragma_table_info(${tableName}) WHERE name = ${columnName}
11
- `.execute(db);
12
- return result.rows.length > 0;
13
- }
14
-
15
- async function tableExists(db: Kysely<unknown>, tableName: string): Promise<boolean> {
16
- const result = await sql<{ name: string }>`
17
- SELECT name FROM sqlite_master WHERE type = 'table' AND name = ${tableName}
18
- `.execute(db);
19
- return result.rows.length > 0;
20
- }
21
-
22
- export async function up(db: Kysely<unknown>): Promise<void> {
23
- // Extend tool_calls table with failure tracking columns
24
- if (!(await columnExists(db, "tool_calls", "status"))) {
25
- await db.schema
26
- .alterTable("tool_calls")
27
- .addColumn("status", "text", (col) => col.defaultTo("success"))
28
- .execute();
29
- }
30
-
31
- if (!(await columnExists(db, "tool_calls", "error_message"))) {
32
- await db.schema.alterTable("tool_calls").addColumn("error_message", "text").execute();
33
- }
34
-
35
- if (!(await columnExists(db, "tool_calls", "error_type"))) {
36
- await db.schema.alterTable("tool_calls").addColumn("error_type", "text").execute();
37
- }
38
-
39
- if (!(await columnExists(db, "tool_calls", "device_id"))) {
40
- await db.schema.alterTable("tool_calls").addColumn("device_id", "text").execute();
41
- }
42
-
43
- if (!(await columnExists(db, "tool_calls", "package_name"))) {
44
- await db.schema.alterTable("tool_calls").addColumn("package_name", "text").execute();
45
- }
46
-
47
- if (!(await columnExists(db, "tool_calls", "duration_ms"))) {
48
- await db.schema.alterTable("tool_calls").addColumn("duration_ms", "integer").execute();
49
- }
50
-
51
- if (!(await columnExists(db, "tool_calls", "tool_args"))) {
52
- await db.schema.alterTable("tool_calls").addColumn("tool_args", "text").execute();
53
- }
54
-
55
- // Create index for failed tool calls
56
- await db.schema
57
- .createIndex("idx_tool_calls_status")
58
- .ifNotExists()
59
- .on("tool_calls")
60
- .column("status")
61
- .ifNotExists()
62
- .execute();
63
-
64
- // Create crashes table
65
- if (!(await tableExists(db, "crashes"))) {
66
- await db.schema
67
- .createTable("crashes")
68
- .ifNotExists()
69
- .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
70
- .addColumn("device_id", "text", (col) => col.notNull())
71
- .addColumn("package_name", "text", (col) => col.notNull())
72
- .addColumn("crash_type", "text", (col) => col.notNull()) // java, native, system
73
- .addColumn("timestamp", "integer", (col) => col.notNull())
74
- .addColumn("process_name", "text")
75
- .addColumn("pid", "integer")
76
- .addColumn("exception_class", "text") // e.g., NullPointerException
77
- .addColumn("exception_message", "text")
78
- .addColumn("stacktrace", "text")
79
- .addColumn("signal", "text") // For native crashes (SIGSEGV, SIGABRT, etc.)
80
- .addColumn("fault_address", "text") // For native crashes
81
- .addColumn("tombstone_path", "text") // Path to tombstone file if available
82
- .addColumn("detection_source", "text", (col) => col.notNull()) // logcat, tombstone, dropbox, accessibility, process_monitor
83
- .addColumn("raw_log", "text") // Raw crash log output
84
- // Nullable FKs for linking to navigation and test runs
85
- .addColumn("navigation_node_id", "integer", (col) =>
86
- col.references("navigation_nodes.id").onDelete("set null"),
87
- )
88
- .addColumn("test_execution_id", "integer", (col) =>
89
- col.references("test_executions.id").onDelete("set null"),
90
- )
91
- .addColumn("session_uuid", "text")
92
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
93
- .execute();
94
-
95
- await db.schema
96
- .createIndex("idx_crashes_device_id")
97
- .ifNotExists()
98
- .on("crashes")
99
- .column("device_id")
100
- .execute();
101
-
102
- await db.schema
103
- .createIndex("idx_crashes_package_name")
104
- .ifNotExists()
105
- .on("crashes")
106
- .column("package_name")
107
- .execute();
108
-
109
- await db.schema
110
- .createIndex("idx_crashes_timestamp")
111
- .ifNotExists()
112
- .on("crashes")
113
- .column("timestamp")
114
- .execute();
115
-
116
- await db.schema
117
- .createIndex("idx_crashes_navigation_node")
118
- .ifNotExists()
119
- .on("crashes")
120
- .column("navigation_node_id")
121
- .execute();
122
-
123
- await db.schema
124
- .createIndex("idx_crashes_test_execution")
125
- .ifNotExists()
126
- .on("crashes")
127
- .column("test_execution_id")
128
- .execute();
129
- }
130
-
131
- // Create ANRs table
132
- if (!(await tableExists(db, "anrs"))) {
133
- await db.schema
134
- .createTable("anrs")
135
- .ifNotExists()
136
- .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
137
- .addColumn("device_id", "text", (col) => col.notNull())
138
- .addColumn("package_name", "text", (col) => col.notNull())
139
- .addColumn("timestamp", "integer", (col) => col.notNull())
140
- .addColumn("process_name", "text")
141
- .addColumn("pid", "integer")
142
- .addColumn("reason", "text") // e.g., "Input dispatching timed out"
143
- .addColumn("activity", "text") // Activity that was ANR'd
144
- .addColumn("wait_duration_ms", "integer") // How long the wait was
145
- .addColumn("cpu_usage", "text") // CPU usage info at time of ANR
146
- .addColumn("main_thread_state", "text") // State of main thread
147
- .addColumn("stacktrace", "text") // Main thread stacktrace
148
- .addColumn("detection_source", "text", (col) => col.notNull()) // logcat, dropbox, accessibility
149
- .addColumn("raw_log", "text") // Raw ANR log output
150
- // Nullable FKs for linking to navigation and test runs
151
- .addColumn("navigation_node_id", "integer", (col) =>
152
- col.references("navigation_nodes.id").onDelete("set null"),
153
- )
154
- .addColumn("test_execution_id", "integer", (col) =>
155
- col.references("test_executions.id").onDelete("set null"),
156
- )
157
- .addColumn("session_uuid", "text")
158
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
159
- .execute();
160
-
161
- await db.schema
162
- .createIndex("idx_anrs_device_id")
163
- .ifNotExists()
164
- .on("anrs")
165
- .column("device_id")
166
- .execute();
167
-
168
- await db.schema
169
- .createIndex("idx_anrs_package_name")
170
- .ifNotExists()
171
- .on("anrs")
172
- .column("package_name")
173
- .execute();
174
-
175
- await db.schema
176
- .createIndex("idx_anrs_timestamp")
177
- .ifNotExists()
178
- .on("anrs")
179
- .column("timestamp")
180
- .execute();
181
-
182
- await db.schema
183
- .createIndex("idx_anrs_navigation_node")
184
- .ifNotExists()
185
- .on("anrs")
186
- .column("navigation_node_id")
187
- .execute();
188
-
189
- await db.schema
190
- .createIndex("idx_anrs_test_execution")
191
- .ifNotExists()
192
- .on("anrs")
193
- .column("test_execution_id")
194
- .execute();
195
- }
196
- }
197
-
198
- export async function down(db: Kysely<unknown>): Promise<void> {
199
- await db.schema.dropTable("anrs").ifExists().execute();
200
- await db.schema.dropTable("crashes").ifExists().execute();
201
-
202
- // Note: SQLite doesn't support DROP COLUMN, so we can't remove the added columns
203
- // from tool_calls. In production, would need to recreate the table without these columns.
204
- }