common-qa-mcp 0.1.0

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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/dist/adapters/factory.d.ts +15 -0
  4. package/dist/adapters/factory.d.ts.map +1 -0
  5. package/dist/adapters/factory.js +18 -0
  6. package/dist/adapters/factory.js.map +1 -0
  7. package/dist/adapters/remoteMcp/adapter.d.ts +92 -0
  8. package/dist/adapters/remoteMcp/adapter.d.ts.map +1 -0
  9. package/dist/adapters/remoteMcp/adapter.js +219 -0
  10. package/dist/adapters/remoteMcp/adapter.js.map +1 -0
  11. package/dist/adapters/types.d.ts +77 -0
  12. package/dist/adapters/types.d.ts.map +1 -0
  13. package/dist/adapters/types.js +2 -0
  14. package/dist/adapters/types.js.map +1 -0
  15. package/dist/adapters/walnut/actionTypes.d.ts +16 -0
  16. package/dist/adapters/walnut/actionTypes.d.ts.map +1 -0
  17. package/dist/adapters/walnut/actionTypes.js +141 -0
  18. package/dist/adapters/walnut/actionTypes.js.map +1 -0
  19. package/dist/adapters/walnut/mapper.d.ts +34 -0
  20. package/dist/adapters/walnut/mapper.d.ts.map +1 -0
  21. package/dist/adapters/walnut/mapper.js +228 -0
  22. package/dist/adapters/walnut/mapper.js.map +1 -0
  23. package/dist/cli.d.ts +3 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +36 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/config.d.ts +81 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +211 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/connection.d.ts +101 -0
  32. package/dist/connection.d.ts.map +1 -0
  33. package/dist/connection.js +190 -0
  34. package/dist/connection.js.map +1 -0
  35. package/dist/domain/types.d.ts +279 -0
  36. package/dist/domain/types.d.ts.map +1 -0
  37. package/dist/domain/types.js +2 -0
  38. package/dist/domain/types.js.map +1 -0
  39. package/dist/domain/validate.d.ts +3 -0
  40. package/dist/domain/validate.d.ts.map +1 -0
  41. package/dist/domain/validate.js +29 -0
  42. package/dist/domain/validate.js.map +1 -0
  43. package/dist/drafts/store.d.ts +62 -0
  44. package/dist/drafts/store.d.ts.map +1 -0
  45. package/dist/drafts/store.js +119 -0
  46. package/dist/drafts/store.js.map +1 -0
  47. package/dist/httpMain.d.ts +3 -0
  48. package/dist/httpMain.d.ts.map +1 -0
  49. package/dist/httpMain.js +22 -0
  50. package/dist/httpMain.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +46 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/initAgents.d.ts +2 -0
  56. package/dist/initAgents.d.ts.map +1 -0
  57. package/dist/initAgents.js +178 -0
  58. package/dist/initAgents.js.map +1 -0
  59. package/dist/log.d.ts +14 -0
  60. package/dist/log.d.ts.map +1 -0
  61. package/dist/log.js +14 -0
  62. package/dist/log.js.map +1 -0
  63. package/dist/mcpResult.d.ts +20 -0
  64. package/dist/mcpResult.d.ts.map +1 -0
  65. package/dist/mcpResult.js +8 -0
  66. package/dist/mcpResult.js.map +1 -0
  67. package/dist/mount.d.ts +12 -0
  68. package/dist/mount.d.ts.map +1 -0
  69. package/dist/mount.js +126 -0
  70. package/dist/mount.js.map +1 -0
  71. package/dist/projectStore.d.ts +5 -0
  72. package/dist/projectStore.d.ts.map +1 -0
  73. package/dist/projectStore.js +60 -0
  74. package/dist/projectStore.js.map +1 -0
  75. package/dist/requestConnection.d.ts +20 -0
  76. package/dist/requestConnection.d.ts.map +1 -0
  77. package/dist/requestConnection.js +44 -0
  78. package/dist/requestConnection.js.map +1 -0
  79. package/dist/serve.d.ts +10 -0
  80. package/dist/serve.d.ts.map +1 -0
  81. package/dist/serve.js +37 -0
  82. package/dist/serve.js.map +1 -0
  83. package/dist/server.d.ts +29 -0
  84. package/dist/server.d.ts.map +1 -0
  85. package/dist/server.js +76 -0
  86. package/dist/server.js.map +1 -0
  87. package/dist/tools/connection.tools.d.ts +16 -0
  88. package/dist/tools/connection.tools.d.ts.map +1 -0
  89. package/dist/tools/connection.tools.js +106 -0
  90. package/dist/tools/connection.tools.js.map +1 -0
  91. package/dist/tools/discovery.tools.d.ts +16 -0
  92. package/dist/tools/discovery.tools.d.ts.map +1 -0
  93. package/dist/tools/discovery.tools.js +369 -0
  94. package/dist/tools/discovery.tools.js.map +1 -0
  95. package/dist/tools/markdown.d.ts +13 -0
  96. package/dist/tools/markdown.d.ts.map +1 -0
  97. package/dist/tools/markdown.js +65 -0
  98. package/dist/tools/markdown.js.map +1 -0
  99. package/dist/tools/recording.tools.d.ts +16 -0
  100. package/dist/tools/recording.tools.d.ts.map +1 -0
  101. package/dist/tools/recording.tools.js +378 -0
  102. package/dist/tools/recording.tools.js.map +1 -0
  103. package/dist/tools/run.tools.d.ts +13 -0
  104. package/dist/tools/run.tools.d.ts.map +1 -0
  105. package/dist/tools/run.tools.js +230 -0
  106. package/dist/tools/run.tools.js.map +1 -0
  107. package/dist/tools/schemas.d.ts +560 -0
  108. package/dist/tools/schemas.d.ts.map +1 -0
  109. package/dist/tools/schemas.js +112 -0
  110. package/dist/tools/schemas.js.map +1 -0
  111. package/dist/tools/testcase.tools.d.ts +50 -0
  112. package/dist/tools/testcase.tools.d.ts.map +1 -0
  113. package/dist/tools/testcase.tools.js +384 -0
  114. package/dist/tools/testcase.tools.js.map +1 -0
  115. package/dist/vendor/actionSignatures.d.ts +117 -0
  116. package/dist/vendor/actionSignatures.d.ts.map +1 -0
  117. package/dist/vendor/actionSignatures.js +420 -0
  118. package/dist/vendor/actionSignatures.js.map +1 -0
  119. package/dist/vendor/logger.d.ts +38 -0
  120. package/dist/vendor/logger.d.ts.map +1 -0
  121. package/dist/vendor/logger.js +174 -0
  122. package/dist/vendor/logger.js.map +1 -0
  123. package/package.json +57 -0
@@ -0,0 +1,141 @@
1
+ import { WEB_ACTION_SIGNATURES } from '../../vendor/actionSignatures.js';
2
+ /**
3
+ * Static per-technology action-type catalogs. Used as the whole `actionsCatalog()`
4
+ * answer for android/api/database/mainframe (there is no live/per-org source for
5
+ * those) and as the `web` fallback when the live per-org `GET /api/actions` catalog
6
+ * is unavailable (see `WalnutAdapter.actionsCatalog` in adapter.ts).
7
+ *
8
+ * Source (copied verbatim -- do not invent action names):
9
+ * - web: walnut-mcp/modules/config/action_types.py, VALID_ACTION_TYPES (lines 12-36)
10
+ * -- the 41 canonical action types mirroring the agent's registered Playwright
11
+ * tool handlers.
12
+ * - android: walnut-mcp/modules/config/platform_action_types.py,
13
+ * ANDROID_ACTION_TYPES (lines 44-91) -- 75 Appium actions.
14
+ * - api: walnut-mcp/modules/config/platform_action_types.py,
15
+ * API_ACTION_TYPES (line 148).
16
+ * - database: walnut-mcp/modules/config/platform_action_types.py,
17
+ * DATABASE_ACTION_TYPES (line 220).
18
+ * - mainframe: no frozenset for this platform exists in either source file --
19
+ * left empty rather than guessed (spec §6A documents a Walnut-only mainframe
20
+ * step_type, but platform_action_types.py has no corresponding action-type set).
21
+ */
22
+ /**
23
+ * The walnut-mcp copy of the web catalog (41 types). Kept verbatim as its own const
24
+ * because it is a COPY of an external source; the live web catalog below unions it
25
+ * with the agent's own signature table instead of editing this list by hand.
26
+ */
27
+ const WALNUT_MCP_WEB_ACTIONS = [
28
+ // Navigation
29
+ 'navigate', 'navigate_back', 'go_forward', 'reload', 'close', 'tabs',
30
+ // Input
31
+ 'click', 'dblclick', 'type', 'fill_form', 'select_option',
32
+ 'press_key', 'tap', 'hover', 'focus', 'blur',
33
+ // Mouse coordinate
34
+ 'mouse_click_xy', 'mouse_drag_xy', 'mouse_move_xy',
35
+ // Drag & Drop
36
+ 'drag',
37
+ // File
38
+ 'file_upload',
39
+ // Verification
40
+ 'verify_element_visible', 'verify_text_visible', 'verify_value',
41
+ 'verify_url_contains', 'verify_list_visible', 'verify_navigation',
42
+ // Wait
43
+ 'wait_for_visible', 'wait_for_hidden', 'wait_for_navigation',
44
+ 'wait_for_url', 'wait_for_attached', 'wait_for_detached', 'wait_timeout',
45
+ // Advanced
46
+ 'evaluate', 'run_code', 'handle_dialog',
47
+ // Layout
48
+ 'scroll', 'resize', 'pdf_save',
49
+ // API
50
+ 'api_call',
51
+ ];
52
+ /**
53
+ * The web catalog the validator and `actions_catalog` both answer from: the
54
+ * walnut-mcp copy UNIONED with every actionType the agent itself can bake
55
+ * (WEB_ACTION_SIGNATURES -- the same table the recorder fills descriptions from).
56
+ *
57
+ * Why the union rather than a longer hand-kept list: the copy above froze at 41
58
+ * types and the agent has since grown 12 more, so a recording that used any of
59
+ * them was reported as INVALID by our own validator -- `verify_compare` and
60
+ * `store_run_variable` are exposed MCP tools, and every draft containing one came
61
+ * back `validation ✗ -- actionType "verify_compare" not in catalog`. Deriving from
62
+ * the signature table means a future action the agent learns to bake is valid here
63
+ * the moment it has a signature, with no second list to remember.
64
+ */
65
+ const WEB_ACTIONS = [
66
+ ...new Set([...WALNUT_MCP_WEB_ACTIONS, ...Object.keys(WEB_ACTION_SIGNATURES)]),
67
+ ];
68
+ export const WALNUT_ACTIONS = {
69
+ web: WEB_ACTIONS,
70
+ android: [
71
+ // App lifecycle
72
+ 'android_start_app', 'android_close_app', 'android_reset_app',
73
+ 'android_install_app', 'android_uninstall_app', 'android_switch_app',
74
+ 'android_is_app_installed',
75
+ // Interaction
76
+ 'android_click', 'android_tap', 'android_longpress', 'android_type',
77
+ 'android_clear', 'android_click_by_text', 'android_click_by_image',
78
+ 'android_click_if_present', 'android_drag_drop',
79
+ 'android_hide_keyboard', 'android_press_key',
80
+ 'android_pinch_in', 'android_pinch_out',
81
+ // Scroll & Swipe
82
+ 'android_scroll_to_element', 'android_scroll_to_text',
83
+ 'android_scroll_to_top', 'android_scroll_to_bottom',
84
+ 'android_scroll_left', 'android_scroll_right',
85
+ 'android_scroll_to_element_and_click',
86
+ 'android_scroll_until_element_gone', 'android_scroll_until_text_gone',
87
+ 'android_swipe', 'android_swipe_by_direction',
88
+ // Navigation
89
+ 'android_back', 'android_home', 'android_recent_apps',
90
+ 'android_open_notification', 'android_switch_context',
91
+ 'android_get_contexts', 'android_get_current_context',
92
+ // Verification
93
+ 'android_validate_text', 'android_validate_partial_text',
94
+ 'android_validate_by_text', 'android_validate_by_image',
95
+ 'android_wait_for_element', 'android_wait_for_element_gone',
96
+ 'android_wait_for_text', 'android_wait_for_text_gone',
97
+ 'android_is_element_visible', 'android_is_element_enabled',
98
+ 'android_is_element_checked', 'android_is_element_clickable',
99
+ 'android_is_element_disabled', 'android_is_element_selected',
100
+ 'android_is_element_not_visible', 'android_is_element_not_checked',
101
+ 'android_is_element_not_selected',
102
+ // Data retrieval
103
+ 'android_get_text', 'android_get_attribute',
104
+ // Device controls
105
+ 'android_take_screenshot', 'android_snapshot',
106
+ 'android_rotate_screen', 'android_device_info',
107
+ 'android_lock', 'android_unlock', 'android_is_locked',
108
+ // Network
109
+ 'android_wifi_status', 'android_toggle_wifi',
110
+ 'android_data_status', 'android_toggle_data',
111
+ 'android_bluetooth_status', 'android_toggle_bluetooth',
112
+ 'android_airplane_mode_status',
113
+ // File operations
114
+ 'android_push_file', 'android_pull_file',
115
+ // ADB
116
+ 'android_adb_command', 'android_type_adb',
117
+ ],
118
+ api: ['api_call'],
119
+ database: ['database_query'],
120
+ mainframe: [],
121
+ };
122
+ /** Flattens a per-platform action-type catalog into `ActionDef[]`, tagging every entry. */
123
+ export function actionDefsFrom(catalog) {
124
+ return Object.entries(catalog).flatMap(([platform, actionTypes]) => actionTypes.map((actionType) => ({ actionType, platform })));
125
+ }
126
+ // Precomputed once at module load -- every draft-tool validation call shares
127
+ // these Sets rather than rebuilding them per call.
128
+ const CATALOG_SETS = Object.fromEntries(Object.entries(WALNUT_ACTIONS).map(([platform, list]) => [platform, new Set(list)]));
129
+ /**
130
+ * The single shared `catalogFor` callback every offline draft tool passes to
131
+ * `validateTestCase` -- no network, no adapter, just the static WALNUT_ACTIONS
132
+ * table above. Maps a neutral `Step.stepType` to its WALNUT_ACTIONS bucket;
133
+ * `control_flow` is exempted by `validateTestCase` itself before this is ever
134
+ * consulted, and a stepType with no offline vocabulary (`function`) yields an
135
+ * empty Set, which `validateTestCase` treats as "nothing to check" rather than
136
+ * "nothing is valid".
137
+ */
138
+ export function offlineCatalogFor(step) {
139
+ return CATALOG_SETS[step.stepType] ?? new Set();
140
+ }
141
+ //# sourceMappingURL=actionTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionTypes.js","sourceRoot":"","sources":["../../../src/adapters/walnut/actionTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Step, TcSummary, TestCase } from '../../domain/types.js';
2
+ export type CfKind = NonNullable<Step['controlFlow']>['kind'];
3
+ export declare const WALNUT_TO_CF: Record<string, CfKind>;
4
+ /** Inverse of WALNUT_TO_CF (neutral controlFlow.kind -> walnut action_type),
5
+ * derived once from the same table -- never maintained by hand. */
6
+ export declare const CF_TO_WALNUT: Record<CfKind, string>;
7
+ /**
8
+ * Maps one walnut wire `test_steps[]` entry (models/schemas.ts `step` schema shape)
9
+ * to a neutral `Step`. `toWalnutStep` (the write direction) lands in Task 6.
10
+ */
11
+ export declare function fromWalnutStep(raw: any): Step;
12
+ /**
13
+ * Maps a walnut `GET /api/test-cases/:id` response document (test-cases.ts:5125,
14
+ * TestCaseSchema-shaped) to the neutral `TestCase`. `toWalnutTc` (the write
15
+ * direction, exact payload shape the backend's zod schema accepts) lands in Task 6.
16
+ */
17
+ export declare function fromWalnutTc(doc: any): TestCase;
18
+ /** Maps one walnut `GET /api/test-cases` list-row (test-cases.ts:1746 `testCases[]`) to a `TcSummary`. */
19
+ export declare function fromWalnutTcSummary(row: any): TcSummary;
20
+ /**
21
+ * One neutral `Step` -> one walnut-frontend draft `test_steps[]` entry.
22
+ * `fromWalnutStep` above is the reverse (though it targets the backend wire
23
+ * shape, not this draft shape -- see the round-trip test in
24
+ * tests/unit/walnut.mapper.test.ts, which exercises both directions and only
25
+ * asserts on stepType/controlFlow.kind, the fields both shapes agree on).
26
+ */
27
+ export declare function toWalnutStep(s: Step): Record<string, unknown>;
28
+ /**
29
+ * Whole neutral TestCase -> the walnut-frontend draft payload's TestCase-level
30
+ * fields. `tc_draft_open` layers `draftId`/`mode` on top of this -- those
31
+ * describe the draft's hosting, not the test case itself.
32
+ */
33
+ export declare function toWalnutTc(tc: TestCase): Record<string, unknown>;
34
+ //# sourceMappingURL=mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../../../src/adapters/walnut/mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAElF,MAAM,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAK9D,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAM/C,CAAC;AAEF;mEACmE;AACnE,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAErB,CAAC;AA0C5B;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAqC7C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,CAoB/C;AAED,0GAA0G;AAC1G,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CAQvD;AA+CD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsC7D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOhE"}
@@ -0,0 +1,228 @@
1
+ // The single source of truth for control-flow action_type <-> kind. Exported (and
2
+ // inverted into CF_TO_WALNUT below) so the forward direction never hand-writes a
3
+ // second literal that could silently drift out of lockstep with this one.
4
+ export const WALNUT_TO_CF = {
5
+ if_start: 'if',
6
+ else: 'else',
7
+ if_end: 'if_end',
8
+ while_start: 'while',
9
+ while_end: 'while_end',
10
+ };
11
+ /** Inverse of WALNUT_TO_CF (neutral controlFlow.kind -> walnut action_type),
12
+ * derived once from the same table -- never maintained by hand. */
13
+ export const CF_TO_WALNUT = Object.fromEntries(Object.entries(WALNUT_TO_CF).map(([wire, kind]) => [kind, wire]));
14
+ const KNOWN_STEP_TYPES = new Set([
15
+ 'web', 'android', 'control_flow', 'function', 'database', 'mainframe',
16
+ ]);
17
+ /**
18
+ * Walnut's wire uses upper-case `'API'` for API steps; the neutral model uses
19
+ * lower-case `'api'`. Any other recognized value passes through unchanged; an
20
+ * absent/unrecognized value defaults to `'web'` (mirrors the backend's own
21
+ * `step_type` schema default in models/schemas.ts).
22
+ */
23
+ function normalizeStepType(raw) {
24
+ if (raw === 'API')
25
+ return 'api';
26
+ if (typeof raw === 'string' && KNOWN_STEP_TYPES.has(raw))
27
+ return raw;
28
+ return 'web';
29
+ }
30
+ function fromWalnutCondition(raw) {
31
+ if (!raw)
32
+ return undefined;
33
+ return {
34
+ type: raw.type,
35
+ selector: raw.selector ?? undefined,
36
+ objectId: raw.object_id ?? undefined,
37
+ text: raw.text ?? undefined,
38
+ variableName: raw.variableName ?? undefined,
39
+ variableValue: raw.variableValue ?? undefined,
40
+ };
41
+ }
42
+ function fromWalnutFlags(raw) {
43
+ const sla = raw.stepLevelActions;
44
+ const hasIsOptional = raw.is_optional !== undefined && raw.is_optional !== null;
45
+ if (!sla && !hasIsOptional)
46
+ return undefined;
47
+ return {
48
+ ...(sla
49
+ ? { skip: !!sla.skip, screenshot: !!sla.captureScreenshot, continueOnFailure: !!sla.continueOnFailure }
50
+ : {}),
51
+ ...(hasIsOptional ? { optional: !!raw.is_optional } : {}),
52
+ };
53
+ }
54
+ /**
55
+ * Maps one walnut wire `test_steps[]` entry (models/schemas.ts `step` schema shape)
56
+ * to a neutral `Step`. `toWalnutStep` (the write direction) lands in Task 6.
57
+ */
58
+ export function fromWalnutStep(raw) {
59
+ const n = raw.step_number;
60
+ const description = raw.step_description ?? '';
61
+ const actionType = raw.action_type;
62
+ const expectedResult = raw.expected_result ?? undefined;
63
+ const flags = fromWalnutFlags(raw);
64
+ const cfKind = WALNUT_TO_CF[actionType];
65
+ if (cfKind) {
66
+ return {
67
+ n,
68
+ description,
69
+ actionType,
70
+ stepType: 'control_flow',
71
+ expectedResult,
72
+ controlFlow: {
73
+ kind: cfKind,
74
+ condition: fromWalnutCondition(raw.condition),
75
+ blockId: raw.block_id ?? undefined,
76
+ maxIterations: raw.max_iterations ?? undefined,
77
+ },
78
+ flags,
79
+ };
80
+ }
81
+ return {
82
+ n,
83
+ description,
84
+ actionType,
85
+ stepType: normalizeStepType(raw.step_type),
86
+ objectRef: raw.object_id != null ? { id: String(raw.object_id) } : undefined,
87
+ value: raw.input_value ?? undefined,
88
+ expectedResult,
89
+ api: raw.api_request_data ?? undefined,
90
+ db: raw.db_request_data ?? undefined,
91
+ flags,
92
+ };
93
+ }
94
+ /**
95
+ * Maps a walnut `GET /api/test-cases/:id` response document (test-cases.ts:5125,
96
+ * TestCaseSchema-shaped) to the neutral `TestCase`. `toWalnutTc` (the write
97
+ * direction, exact payload shape the backend's zod schema accepts) lands in Task 6.
98
+ */
99
+ export function fromWalnutTc(doc) {
100
+ const id = doc.test_case_id ?? (doc._id != null ? String(doc._id) : undefined);
101
+ const category = doc.test_category === 'manual' || doc.test_category === 'playwright' ? doc.test_category : 'automated';
102
+ return {
103
+ id,
104
+ title: doc.title,
105
+ description: doc.description ?? undefined,
106
+ objective: doc.objective ?? undefined,
107
+ priority: doc.priority,
108
+ status: doc.status ?? undefined,
109
+ category,
110
+ preconditions: doc.preconditions ?? undefined,
111
+ expectedOutcome: doc.expected_outcome ?? undefined,
112
+ tags: doc.tags ?? [],
113
+ // Passthrough for Task 6's optimistic-concurrency PUT retry -- dropping this
114
+ // silently overwrites concurrent edits.
115
+ version: doc.version ?? undefined,
116
+ steps: (doc.test_steps ?? []).map(fromWalnutStep),
117
+ };
118
+ }
119
+ /** Maps one walnut `GET /api/test-cases` list-row (test-cases.ts:1746 `testCases[]`) to a `TcSummary`. */
120
+ export function fromWalnutTcSummary(row) {
121
+ return {
122
+ id: row.test_case_id ?? String(row._id),
123
+ title: row.title,
124
+ priority: row.priority,
125
+ status: row.status ?? undefined,
126
+ stepCount: row.total_steps ?? undefined,
127
+ };
128
+ }
129
+ // ---------------------------------------------------------------------------
130
+ // FORWARD direction: neutral -> walnut, for the offline draft hand-off.
131
+ //
132
+ // This is NOT the backend's persistence wire shape (`POST/PUT /api/test-cases`,
133
+ // which uses `input_value`/`object_id` snake_case and lands with the write-side
134
+ // WalnutAdapter in a later task). It is the exact shape walnut-frontend's real
135
+ // TestStepsEditor consumes off the `/embed/test-case-draft#d=<payload>` URL
136
+ // fragment -- reproduced here from scripts/open-in-walnut-editor.ts's hand-built
137
+ // literal so there is one real implementation instead of a throwaway script and
138
+ // a second hand-written literal drifting apart from it.
139
+ // ---------------------------------------------------------------------------
140
+ const PARAM_TOKEN = /\$\{([^}]+)\}/g;
141
+ /** Every distinct `${name}` token in a step description, in first-seen order. */
142
+ function detectParams(description) {
143
+ const found = [];
144
+ for (const m of (description ?? '').matchAll(PARAM_TOKEN)) {
145
+ const name = m[1];
146
+ if (!found.includes(name))
147
+ found.push(name);
148
+ }
149
+ return found;
150
+ }
151
+ /** Forward of fromWalnutCondition: neutral Condition -> walnut condition (object_id snake_case). */
152
+ function toWalnutCondition(c) {
153
+ if (!c)
154
+ return undefined;
155
+ return {
156
+ type: c.type,
157
+ ...(c.selector !== undefined ? { selector: c.selector } : {}),
158
+ ...(c.objectId !== undefined ? { object_id: c.objectId } : {}),
159
+ ...(c.text !== undefined ? { text: c.text } : {}),
160
+ ...(c.variableName !== undefined ? { variableName: c.variableName } : {}),
161
+ ...(c.variableValue !== undefined ? { variableValue: c.variableValue } : {}),
162
+ };
163
+ }
164
+ /** Forward of fromWalnutFlags's stepLevelActions branch. `is_optional` is
165
+ * deliberately NOT emitted -- it never appears in the editor draft payload
166
+ * scripts/open-in-walnut-editor.ts models, only in the backend persistence
167
+ * wire shape (a different contract, out of scope here). */
168
+ function toWalnutFlags(flags) {
169
+ return { skip: !!flags.skip, captureScreenshot: !!flags.screenshot, continueOnFailure: !!flags.continueOnFailure };
170
+ }
171
+ /**
172
+ * One neutral `Step` -> one walnut-frontend draft `test_steps[]` entry.
173
+ * `fromWalnutStep` above is the reverse (though it targets the backend wire
174
+ * shape, not this draft shape -- see the round-trip test in
175
+ * tests/unit/walnut.mapper.test.ts, which exercises both directions and only
176
+ * asserts on stepType/controlFlow.kind, the fields both shapes agree on).
177
+ */
178
+ export function toWalnutStep(s) {
179
+ const cf = s.controlFlow;
180
+ const base = {
181
+ step_number: s.n,
182
+ step_type: s.stepType === 'api' ? 'API' : s.stepType,
183
+ action_type: cf ? CF_TO_WALNUT[cf.kind] : s.actionType,
184
+ step_description: s.description ?? '',
185
+ expected_result: s.expectedResult ?? '',
186
+ };
187
+ if (s.stepType === 'control_flow' && cf) {
188
+ const condition = toWalnutCondition(cf.condition);
189
+ return {
190
+ ...base,
191
+ ...(condition ? { condition } : {}),
192
+ ...(cf.blockId ? { block_id: cf.blockId } : {}),
193
+ ...(cf.maxIterations != null ? { max_iterations: cf.maxIterations } : {}),
194
+ };
195
+ }
196
+ const params = detectParams(s.description);
197
+ // Real recorded locator, when this step originated from a LIVE recording
198
+ // session (mcp/qa/tools/recording.tools.ts) rather than LLM-authored text --
199
+ // `objectRef.locators` is only ever populated in that path (see
200
+ // fromRecordedStep below). `element.locator_type`/`locator_value` is exactly
201
+ // the field walnut-frontend's TestStepsEditor `TestStep.element` reads
202
+ // (TestStepsEditor.tsx ~line 2007) -- prefer the locator flagged `unique`
203
+ // (the same selector Walnut's own replay ladder prioritizes), else the first.
204
+ const primaryLocator = s.objectRef?.locators?.find((l) => l.unique) ?? s.objectRef?.locators?.[0];
205
+ return {
206
+ ...base,
207
+ ...(s.value !== undefined ? { inputValue: s.value } : {}),
208
+ ...(params.length ? { parameterName: params[0], detected_parameters: params } : {}),
209
+ ...(s.api ? { api_request_data: s.api } : {}),
210
+ ...(s.flags ? { stepLevelActions: toWalnutFlags(s.flags) } : {}),
211
+ ...(s.objectRef?.id !== undefined ? { object_id: s.objectRef.id } : {}),
212
+ ...(primaryLocator ? { element: { locator_type: primaryLocator.name, locator_value: primaryLocator.value } } : {}),
213
+ };
214
+ }
215
+ /**
216
+ * Whole neutral TestCase -> the walnut-frontend draft payload's TestCase-level
217
+ * fields. `tc_draft_open` layers `draftId`/`mode` on top of this -- those
218
+ * describe the draft's hosting, not the test case itself.
219
+ */
220
+ export function toWalnutTc(tc) {
221
+ return {
222
+ title: tc.title,
223
+ priority: tc.priority,
224
+ category: tc.category,
225
+ test_steps: tc.steps.map(toWalnutStep),
226
+ };
227
+ }
228
+ //# sourceMappingURL=mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.js","sourceRoot":"","sources":["../../../src/adapters/walnut/mapper.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `common-qa-mcp` CLI — the published bin. Two jobs, Playwright-style:
4
+ * • (default / `serve`) run the Common QA MCP over stdio (what an AI client launches via npx)
5
+ * • `init-agents --loop=<c>` install the Walnut QA agent into your project for a given AI client
6
+ *
7
+ * stdout is the MCP transport in serve mode, so this file writes ONLY to stderr.
8
+ */
9
+ import { startStdioServer } from './serve.js';
10
+ import { runInitAgents } from './initAgents.js';
11
+ function printUsage() {
12
+ process.stderr.write('common-qa-mcp — Common QA MCP\n\n' +
13
+ 'Usage:\n' +
14
+ ' common-qa-mcp [serve] Start the MCP server over stdio (default; AI clients launch this)\n' +
15
+ ' common-qa-mcp init-agents --loop=<c> Install the Walnut QA agent into this project\n' +
16
+ ' <c> = claude | cursor | windsurf | vscode | codex | all\n');
17
+ }
18
+ async function main() {
19
+ const argv = process.argv.slice(2);
20
+ const cmd = argv[0];
21
+ if (cmd === 'init-agents') {
22
+ await runInitAgents(argv.slice(1));
23
+ return;
24
+ }
25
+ if (cmd === undefined || cmd === 'serve') {
26
+ await startStdioServer();
27
+ return;
28
+ }
29
+ printUsage();
30
+ process.exitCode = cmd === 'help' || cmd === '--help' || cmd === '-h' ? 0 : 1;
31
+ }
32
+ main().catch((e) => {
33
+ process.stderr.write(`${e instanceof Error ? (e.stack ?? e.message) : String(e)}\n`);
34
+ process.exit(1);
35
+ });
36
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,SAAS,UAAU;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mCAAmC;QACjC,UAAU;QACV,8GAA8G;QAC9G,0FAA0F;QAC1F,oGAAoG,CACvG,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;QAC1B,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACzC,MAAM,gBAAgB,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IACD,UAAU,EAAE,CAAC;IACb,OAAO,CAAC,QAAQ,GAAG,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,81 @@
1
+ type ConfigCommon = {
2
+ provider?: 'walnut' | 'simplifyqa';
3
+ customerId?: string;
4
+ moduleId?: string;
5
+ walnutAuth: 'key' | 'jwt';
6
+ caps: Set<string>;
7
+ port?: number;
8
+ connection?: string;
9
+ /** Origin of a running walnut-frontend, used by tc_draft_open to build the
10
+ * `/embed/test-case-draft` URL. Optional in both modes -- offline mode has no
11
+ * backend to derive one from, so this is the only source; connected mode may
12
+ * still want to point at a different UI host than the API origin.
13
+ *
14
+ * Also read by `recording_start` (mcp/qa/tools/recording.tools.ts) as an
15
+ * OVERRIDE: when set (env `QA_MCP_WALNUT_UI`), the live recording editor URL
16
+ * points at this deployed frontend's `/embed/test-case-draft` instead of the
17
+ * agent's own self-hosted `/editor` (the default when this is absent). */
18
+ walnutUiOrigin?: string;
19
+ /** Loopback base for the local Walnut agent, which can host the editor window.
20
+ * Only consulted by the standalone stdio dev harness (src/mcp/qa/index.ts) --
21
+ * when this tool surface is mounted in-process inside the agent itself (the
22
+ * product path, see src/mcp/qa/mount.ts), `tc_draft_open` calls the window
23
+ * opener directly and never reads this field. */
24
+ agentUrl?: string;
25
+ };
26
+ /**
27
+ * "No credentials at all" -- the deliberate, error-free mode this server boots
28
+ * into when QA_MCP_URL/TOKEN/PROJECT_ID are all absent (spec: author drafts and
29
+ * hand off to walnut-frontend's editor, no backend, no network). `url`/`token`/
30
+ * `projectId` may still be partially present here in principle, but resolveConfig
31
+ * never returns this shape in that case -- partial credentials are a
32
+ * misconfiguration and throw instead (see resolveConfig).
33
+ */
34
+ export type OfflineQaConfig = ConfigCommon & {
35
+ mode: 'offline';
36
+ url?: string;
37
+ token?: string;
38
+ projectId?: string;
39
+ };
40
+ /** url + token present -- the shape a live ProviderAdapter is built from. `projectId` is OPTIONAL:
41
+ * a session can be connected with NO project selected yet, and the user picks one at runtime
42
+ * (list_projects -> qa_switch_project). Only list_projects works with no project; every other
43
+ * project-scoped op asks the user to choose one first. */
44
+ export type ConnectedQaConfig = ConfigCommon & {
45
+ mode: 'connected';
46
+ url: string;
47
+ token: string;
48
+ projectId?: string;
49
+ };
50
+ export type QaConfig = OfflineQaConfig | ConnectedQaConfig;
51
+ /**
52
+ * Resolution order (spec §12): defaults -> ~/.common-qa-mcp/config.json
53
+ * (named connections; cliFlags.connection selects) -> QA_MCP_* env -> CLI flags.
54
+ * Each layer only overrides keys it actually sets.
55
+ *
56
+ * Credentials (url/token) are optional overall:
57
+ * - BOTH url+token ABSENT -> OfflineQaConfig, no throw. The normal, supported way
58
+ * to run with no backend at all (author drafts, hand off to the editor).
59
+ * - ONE of url/token present but not the other -> misconfiguration, throws naming
60
+ * the missing var.
61
+ * - url+token present -> ConnectedQaConfig. projectId is OPTIONAL: absent means
62
+ * "connected, no project selected yet" and the user chooses one at runtime.
63
+ * Never logs `token` -- callers must go through `mask()` from log.ts if they log
64
+ * config at all.
65
+ */
66
+ export declare function resolveConfig(env: NodeJS.ProcessEnv, cliFlags?: Partial<QaConfig>): QaConfig;
67
+ /**
68
+ * Best-effort provider auto-detect (spec §3: explicit config always wins over this).
69
+ * Runs both probes concurrently (never short-circuits) and only returns a provider when
70
+ * exactly one of them gives a confident match. Returns null on any ambiguity -- including
71
+ * ties -- so the caller surfaces `describeAmbiguousProvider` instead of guessing.
72
+ */
73
+ export declare function detectProvider(url: string, fetchImpl?: typeof fetch): Promise<'walnut' | 'simplifyqa' | null>;
74
+ /**
75
+ * The exact message a caller (the adapter factory, Task 5/6/11/12) should surface when
76
+ * `detectProvider` returns null -- spec §3: "on ambiguity, error asking for QA_MCP_PROVIDER".
77
+ * `detectProvider` itself never throws; this is a pure formatter for whoever does.
78
+ */
79
+ export declare function describeAmbiguousProvider(url: string): string;
80
+ export {};
81
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,GAAG,KAAK,CAAC;IAC1B,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;8EAQ0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;qDAIiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG;IAC3C,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;2DAG2D;AAC3D,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG;IAC7C,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAgH3D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,QAAQ,CAyBhG;AA4BD;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,QAAQ,GAAG,YAAY,GAAG,IAAI,CAAC,CAoBzC;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK7D"}