@sun-asterisk/sungen 3.1.2-beta.99 → 3.2.0-beta.141

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 (192) hide show
  1. package/dist/capabilities/context.d.ts +17 -0
  2. package/dist/capabilities/context.d.ts.map +1 -1
  3. package/dist/capabilities/discover.d.ts.map +1 -1
  4. package/dist/capabilities/discover.js +70 -9
  5. package/dist/capabilities/discover.js.map +1 -1
  6. package/dist/capabilities/registry.d.ts +3 -1
  7. package/dist/capabilities/registry.d.ts.map +1 -1
  8. package/dist/capabilities/registry.js.map +1 -1
  9. package/dist/capabilities/sensor.d.ts +3 -0
  10. package/dist/capabilities/sensor.d.ts.map +1 -1
  11. package/dist/cli/commands/audit.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.js +17 -11
  13. package/dist/cli/commands/audit.js.map +1 -1
  14. package/dist/cli/commands/capability.d.ts.map +1 -1
  15. package/dist/cli/commands/capability.js +57 -5
  16. package/dist/cli/commands/capability.js.map +1 -1
  17. package/dist/cli/commands/context.d.ts +9 -0
  18. package/dist/cli/commands/context.d.ts.map +1 -0
  19. package/dist/cli/commands/context.js +91 -0
  20. package/dist/cli/commands/context.js.map +1 -0
  21. package/dist/cli/commands/delivery.d.ts.map +1 -1
  22. package/dist/cli/commands/delivery.js +42 -30
  23. package/dist/cli/commands/delivery.js.map +1 -1
  24. package/dist/cli/commands/generate.d.ts.map +1 -1
  25. package/dist/cli/commands/generate.js +28 -5
  26. package/dist/cli/commands/generate.js.map +1 -1
  27. package/dist/cli/commands/ledger.d.ts.map +1 -1
  28. package/dist/cli/commands/ledger.js +15 -5
  29. package/dist/cli/commands/ledger.js.map +1 -1
  30. package/dist/cli/commands/manifest.d.ts.map +1 -1
  31. package/dist/cli/commands/manifest.js +10 -9
  32. package/dist/cli/commands/manifest.js.map +1 -1
  33. package/dist/cli/commands/repair.d.ts +8 -0
  34. package/dist/cli/commands/repair.d.ts.map +1 -0
  35. package/dist/cli/commands/repair.js +97 -0
  36. package/dist/cli/commands/repair.js.map +1 -0
  37. package/dist/cli/commands/script-check.d.ts.map +1 -1
  38. package/dist/cli/commands/script-check.js +13 -9
  39. package/dist/cli/commands/script-check.js.map +1 -1
  40. package/dist/cli/commands/trace.d.ts.map +1 -1
  41. package/dist/cli/commands/trace.js +7 -4
  42. package/dist/cli/commands/trace.js.map +1 -1
  43. package/dist/cli/index.js +4 -0
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/generators/test-generator/code-generator.d.ts +7 -0
  46. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  47. package/dist/generators/test-generator/code-generator.js +112 -42
  48. package/dist/generators/test-generator/code-generator.js.map +1 -1
  49. package/dist/harness/annotation-overrides.d.ts +3 -1
  50. package/dist/harness/annotation-overrides.d.ts.map +1 -1
  51. package/dist/harness/annotation-overrides.js +3 -1
  52. package/dist/harness/annotation-overrides.js.map +1 -1
  53. package/dist/harness/audit.d.ts +9 -1
  54. package/dist/harness/audit.d.ts.map +1 -1
  55. package/dist/harness/audit.js +114 -12
  56. package/dist/harness/audit.js.map +1 -1
  57. package/dist/harness/capability-plan.d.ts +14 -0
  58. package/dist/harness/capability-plan.d.ts.map +1 -1
  59. package/dist/harness/capability-plan.js +63 -1
  60. package/dist/harness/capability-plan.js.map +1 -1
  61. package/dist/harness/data-driven-lint.d.ts.map +1 -1
  62. package/dist/harness/data-driven-lint.js +23 -0
  63. package/dist/harness/data-driven-lint.js.map +1 -1
  64. package/dist/harness/flow-check.d.ts +9 -0
  65. package/dist/harness/flow-check.d.ts.map +1 -1
  66. package/dist/harness/flow-check.js +13 -6
  67. package/dist/harness/flow-check.js.map +1 -1
  68. package/dist/harness/intent.d.ts +6 -0
  69. package/dist/harness/intent.d.ts.map +1 -1
  70. package/dist/harness/intent.js +20 -4
  71. package/dist/harness/intent.js.map +1 -1
  72. package/dist/harness/ledger.d.ts.map +1 -1
  73. package/dist/harness/ledger.js +3 -2
  74. package/dist/harness/ledger.js.map +1 -1
  75. package/dist/harness/manifest.d.ts.map +1 -1
  76. package/dist/harness/manifest.js +3 -2
  77. package/dist/harness/manifest.js.map +1 -1
  78. package/dist/harness/parse.d.ts +1 -0
  79. package/dist/harness/parse.d.ts.map +1 -1
  80. package/dist/harness/parse.js +3 -0
  81. package/dist/harness/parse.js.map +1 -1
  82. package/dist/harness/quality-gates.js +1 -1
  83. package/dist/harness/quality-gates.js.map +1 -1
  84. package/dist/harness/query-catalog.d.ts.map +1 -1
  85. package/dist/harness/query-catalog.js +0 -0
  86. package/dist/harness/query-catalog.js.map +1 -1
  87. package/dist/harness/repair.d.ts +20 -0
  88. package/dist/harness/repair.d.ts.map +1 -0
  89. package/dist/harness/repair.js +111 -0
  90. package/dist/harness/repair.js.map +1 -0
  91. package/dist/harness/script-check.d.ts +3 -1
  92. package/dist/harness/script-check.d.ts.map +1 -1
  93. package/dist/harness/script-check.js +22 -8
  94. package/dist/harness/script-check.js.map +1 -1
  95. package/dist/harness/sensors.d.ts +40 -0
  96. package/dist/harness/sensors.d.ts.map +1 -1
  97. package/dist/harness/sensors.js +54 -2
  98. package/dist/harness/sensors.js.map +1 -1
  99. package/dist/harness/trace.d.ts.map +1 -1
  100. package/dist/harness/trace.js +4 -3
  101. package/dist/harness/trace.js.map +1 -1
  102. package/dist/harness/unit-paths.d.ts +3 -0
  103. package/dist/harness/unit-paths.d.ts.map +1 -0
  104. package/dist/harness/unit-paths.js +52 -0
  105. package/dist/harness/unit-paths.js.map +1 -0
  106. package/dist/index.d.ts +4 -2
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +5 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
  111. package/dist/orchestrator/ai-rules-updater.js +2 -0
  112. package/dist/orchestrator/ai-rules-updater.js.map +1 -1
  113. package/dist/orchestrator/context-discovery.d.ts +12 -0
  114. package/dist/orchestrator/context-discovery.d.ts.map +1 -0
  115. package/dist/orchestrator/context-discovery.js +46 -0
  116. package/dist/orchestrator/context-discovery.js.map +1 -0
  117. package/dist/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +7 -1
  118. package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +6 -2
  119. package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +18 -1
  120. package/dist/orchestrator/templates/ai-instructions/claude-skill-api-design.md +62 -0
  121. package/dist/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +2 -1
  122. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +16 -2
  123. package/dist/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +14 -0
  124. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +6 -2
  125. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +11 -1
  126. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-api-design.md +62 -0
  127. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +2 -1
  128. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +16 -2
  129. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +14 -0
  130. package/dist/orchestrator/templates/specs-api.d.ts +38 -2
  131. package/dist/orchestrator/templates/specs-api.d.ts.map +1 -1
  132. package/dist/orchestrator/templates/specs-api.js +65 -22
  133. package/dist/orchestrator/templates/specs-api.js.map +1 -1
  134. package/dist/orchestrator/templates/specs-api.ts +71 -18
  135. package/dist/orchestrator/templates/specs-db.d.ts +3 -0
  136. package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
  137. package/dist/orchestrator/templates/specs-db.js +78 -1
  138. package/dist/orchestrator/templates/specs-db.js.map +1 -1
  139. package/dist/orchestrator/templates/specs-db.ts +78 -1
  140. package/dist/orchestrator/templates/specs-test-data.ts +2 -1
  141. package/package.json +2 -2
  142. package/src/capabilities/context.ts +19 -0
  143. package/src/capabilities/discover.ts +27 -7
  144. package/src/capabilities/registry.ts +3 -1
  145. package/src/capabilities/sensor.ts +1 -1
  146. package/src/cli/commands/audit.ts +15 -9
  147. package/src/cli/commands/capability.ts +53 -5
  148. package/src/cli/commands/context.ts +52 -0
  149. package/src/cli/commands/delivery.ts +40 -31
  150. package/src/cli/commands/generate.ts +30 -5
  151. package/src/cli/commands/ledger.ts +13 -5
  152. package/src/cli/commands/manifest.ts +9 -7
  153. package/src/cli/commands/repair.ts +57 -0
  154. package/src/cli/commands/script-check.ts +12 -8
  155. package/src/cli/commands/trace.ts +7 -4
  156. package/src/cli/index.ts +4 -0
  157. package/src/generators/test-generator/code-generator.ts +115 -40
  158. package/src/harness/annotation-overrides.ts +3 -1
  159. package/src/harness/audit.ts +115 -15
  160. package/src/harness/capability-plan.ts +51 -1
  161. package/src/harness/data-driven-lint.ts +20 -0
  162. package/src/harness/flow-check.ts +15 -6
  163. package/src/harness/intent.ts +25 -4
  164. package/src/harness/ledger.ts +3 -2
  165. package/src/harness/manifest.ts +3 -2
  166. package/src/harness/parse.ts +4 -0
  167. package/src/harness/quality-gates.ts +1 -1
  168. package/src/harness/query-catalog.ts +0 -0
  169. package/src/harness/repair.ts +75 -0
  170. package/src/harness/script-check.ts +25 -8
  171. package/src/harness/sensors.ts +71 -2
  172. package/src/harness/trace.ts +4 -3
  173. package/src/harness/unit-paths.ts +14 -0
  174. package/src/index.ts +4 -2
  175. package/src/orchestrator/ai-rules-updater.ts +2 -0
  176. package/src/orchestrator/context-discovery.ts +50 -0
  177. package/src/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +7 -1
  178. package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +6 -2
  179. package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +18 -1
  180. package/src/orchestrator/templates/ai-instructions/claude-skill-api-design.md +62 -0
  181. package/src/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +2 -1
  182. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +16 -2
  183. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +14 -0
  184. package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +6 -2
  185. package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +11 -1
  186. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-api-design.md +62 -0
  187. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +2 -1
  188. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +16 -2
  189. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +14 -0
  190. package/src/orchestrator/templates/specs-api.ts +71 -18
  191. package/src/orchestrator/templates/specs-db.ts +78 -1
  192. package/src/orchestrator/templates/specs-test-data.ts +2 -1
@@ -48,6 +48,7 @@ exports.api = void 0;
48
48
  */
49
49
  const fs = __importStar(require("fs"));
50
50
  const path = __importStar(require("path"));
51
+ const test_1 = require("@playwright/test");
51
52
  function loadEnvQa() {
52
53
  for (const name of ['.env.qa', `.env.qa.${process.env.SUNGEN_ENV || ''}`]) {
53
54
  const p = path.join(process.cwd(), name);
@@ -89,39 +90,81 @@ class ApiClient {
89
90
  }
90
91
  return { key, conf };
91
92
  }
92
- /** Run a catalog request and return the response. `req` is embedded at compile time; `params` bind at runtime. */
93
- async call(label, req, params = {}) {
93
+ /**
94
+ * Run a catalog request and return the response. `req` is embedded at compile time; `params` (path
95
+ * `:id`, JSON body `:fields`, and header `:tokens`) bind at runtime. Catalog `headers` layer over the
96
+ * datasource headers and may carry `:param` placeholders — e.g. `authorization: "Bearer :token"` with
97
+ * the dynamic token threaded from a prior response (flow chaining).
98
+ */
99
+ async call(label, req, params = {}, opts = {}) {
94
100
  const { conf } = this.cfg(req.datasource);
95
101
  const base = (conf.base_url || conf.baseUrl || '').replace(/\/$/, '');
96
102
  if (!base)
97
103
  throw new Error(`API Driver: ${label} — datasource has no base_url (set it in .env.qa).`);
98
- const url = base + substitute(req.path, params);
99
- let body;
104
+ const urlPath = substitute(req.path, params); // path params (:id) bind at runtime
100
105
  const headers = { ...(conf.headers || {}) };
106
+ // catalog headers; :param tokens bind at runtime — raw (no URL-encoding, unlike the path)
107
+ for (const [k, v] of Object.entries(req.headers || {}))
108
+ headers[k] = String(v).replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, (_m, p) => String(params[p] ?? ''));
109
+ // Body: substitute `:param` into the body template (object values), then encode per `encoding`.
110
+ let body;
101
111
  if (req.body !== undefined && req.body !== null) {
102
- const resolved = JSON.parse(JSON.stringify(req.body).replace(/":([A-Za-z_][A-Za-z0-9_]*)"/g, (_m, p) => JSON.stringify(params[p] ?? null)));
103
- body = JSON.stringify(resolved);
104
- if (!headers['content-type'] && !headers['Content-Type'])
105
- headers['content-type'] = 'application/json';
112
+ body = JSON.parse(JSON.stringify(req.body).replace(/":([A-Za-z_][A-Za-z0-9_]*)"/g, (_m, p) => JSON.stringify(params[p] ?? null)));
106
113
  }
107
- const controller = new AbortController();
108
- const timer = setTimeout(() => controller.abort(), conf.timeout_ms ?? 15000);
109
- let res;
114
+ // Map the wire format to the right Playwright option (#345): json → data (application/json,
115
+ // default), form form (application/x-www-form-urlencoded), multipart multipart (form-data).
116
+ const bodyOpt = {};
117
+ if (body !== undefined) {
118
+ const enc = req.encoding ?? 'json';
119
+ if (enc === 'form')
120
+ bodyOpt.form = body;
121
+ else if (enc === 'multipart')
122
+ bodyOpt.multipart = body;
123
+ else
124
+ bodyOpt.data = body;
125
+ }
126
+ // Playwright APIRequestContext: same runner/report/retries as UI tests. @hybrid passes
127
+ // `storageState` (the @auth role's saved session) so the request shares the browser's
128
+ // authenticated cookies. Disposed per call so no request context lingers and hangs the process.
129
+ const ctx = await test_1.request.newContext({
130
+ baseURL: base,
131
+ extraHTTPHeaders: headers,
132
+ timeout: conf.timeout_ms ?? 15000,
133
+ ...(opts.storageState ? { storageState: opts.storageState } : {}),
134
+ });
110
135
  try {
111
- res = await fetch(url, { method: req.method, headers, body, signal: controller.signal });
136
+ const res = await ctx.fetch(urlPath, { method: req.method, ...bodyOpt });
137
+ const text = await res.text();
138
+ let parsed = text;
139
+ try {
140
+ parsed = text ? JSON.parse(text) : null;
141
+ }
142
+ catch { /* non-JSON → keep text */ }
143
+ return { status: res.status(), ok: res.ok(), body: parsed, headers: res.headers() };
112
144
  }
113
145
  finally {
114
- clearTimeout(timer);
146
+ await ctx.dispose();
115
147
  }
116
- const text = await res.text();
117
- let parsed = text;
118
- try {
119
- parsed = text ? JSON.parse(text) : null;
120
- }
121
- catch { /* non-JSON keep text */ }
122
- const outHeaders = {};
123
- res.headers.forEach((v, k) => { outHeaders[k] = v; });
124
- return { status: res.status, ok: res.ok, body: parsed, headers: outHeaders };
148
+ }
149
+ /**
150
+ * Fire the same request N times in parallel (the `@concurrent:N` primitive) and bind aggregates —
151
+ * the idempotency/race oracle. Returns the full `responses` array plus `ok_count`, `status_counts`,
152
+ * and `statuses`, asserted with `expect {{name.ok_count}} is 1` (and cross-checked against the DB via
153
+ * `@query` to prove "exactly one charge"). Path access works on the bound value: `{{name.ok_count}}`,
154
+ * `{{name.status_counts.409}}`, `{{name.responses.count}}`, `{{name.responses[0].body.id}}`.
155
+ */
156
+ async callN(label, req, params = {}, n = 1, opts = {}) {
157
+ const count = Math.max(1, Math.floor(n));
158
+ const responses = await Promise.all(Array.from({ length: count }, () => this.call(label, req, params, opts)));
159
+ const status_counts = {};
160
+ for (const r of responses)
161
+ status_counts[String(r.status)] = (status_counts[String(r.status)] || 0) + 1;
162
+ return {
163
+ responses,
164
+ ok_count: responses.filter((r) => r.ok).length,
165
+ status_counts,
166
+ statuses: responses.map((r) => r.status),
167
+ };
125
168
  }
126
169
  }
127
170
  exports.api = new ApiClient();
@@ -1 +1 @@
1
- {"version":3,"file":"specs-api.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"specs-api.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-api.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import * as fs from 'fs';
14
14
  import * as path from 'path';
15
+ import { request, type APIRequestContext } from '@playwright/test';
15
16
 
16
17
  interface ApiDataSource {
17
18
  kind?: string;
@@ -62,39 +63,91 @@ class ApiClient {
62
63
  return { key, conf };
63
64
  }
64
65
 
65
- /** Run a catalog request and return the response. `req` is embedded at compile time; `params` bind at runtime. */
66
+ /**
67
+ * Run a catalog request and return the response. `req` is embedded at compile time; `params` (path
68
+ * `:id`, JSON body `:fields`, and header `:tokens`) bind at runtime. Catalog `headers` layer over the
69
+ * datasource headers and may carry `:param` placeholders — e.g. `authorization: "Bearer :token"` with
70
+ * the dynamic token threaded from a prior response (flow chaining).
71
+ */
66
72
  async call(
67
73
  label: string,
68
- req: { method: string; path: string; body?: unknown; datasource?: string },
74
+ req: { method: string; path: string; body?: unknown; encoding?: 'json' | 'form' | 'multipart'; headers?: Record<string, string>; datasource?: string },
69
75
  params: Record<string, any> = {},
76
+ opts: { storageState?: string } = {},
70
77
  ): Promise<{ status: number; ok: boolean; body: any; headers: Record<string, string> }> {
71
78
  const { conf } = this.cfg(req.datasource);
72
79
  const base = (conf.base_url || conf.baseUrl || '').replace(/\/$/, '');
73
80
  if (!base) throw new Error(`API Driver: ${label} — datasource has no base_url (set it in .env.qa).`);
74
- const url = base + substitute(req.path, params);
81
+ const urlPath = substitute(req.path, params); // path params (:id) bind at runtime
75
82
 
76
- let body: string | undefined;
77
83
  const headers: Record<string, string> = { ...(conf.headers || {}) };
84
+ // catalog headers; :param tokens bind at runtime — raw (no URL-encoding, unlike the path)
85
+ for (const [k, v] of Object.entries(req.headers || {}))
86
+ headers[k] = String(v).replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, (_m, p) => String(params[p] ?? ''));
87
+ // Body: substitute `:param` into the body template (object values), then encode per `encoding`.
88
+ let body: any;
78
89
  if (req.body !== undefined && req.body !== null) {
79
- const resolved = JSON.parse(JSON.stringify(req.body).replace(/":([A-Za-z_][A-Za-z0-9_]*)"/g, (_m, p) => JSON.stringify(params[p] ?? null)));
80
- body = JSON.stringify(resolved);
81
- if (!headers['content-type'] && !headers['Content-Type']) headers['content-type'] = 'application/json';
90
+ body = JSON.parse(JSON.stringify(req.body).replace(/":([A-Za-z_][A-Za-z0-9_]*)"/g, (_m, p) => JSON.stringify(params[p] ?? null)));
91
+ }
92
+ // Map the wire format to the right Playwright option (#345): json data (application/json,
93
+ // default), form → form (application/x-www-form-urlencoded), multipart → multipart (form-data).
94
+ const bodyOpt: Record<string, unknown> = {};
95
+ if (body !== undefined) {
96
+ const enc = req.encoding ?? 'json';
97
+ if (enc === 'form') bodyOpt.form = body;
98
+ else if (enc === 'multipart') bodyOpt.multipart = body;
99
+ else bodyOpt.data = body;
82
100
  }
83
101
 
84
- const controller = new AbortController();
85
- const timer = setTimeout(() => controller.abort(), conf.timeout_ms ?? 15000);
86
- let res: Response;
102
+ // Playwright APIRequestContext: same runner/report/retries as UI tests. @hybrid passes
103
+ // `storageState` (the @auth role's saved session) so the request shares the browser's
104
+ // authenticated cookies. Disposed per call so no request context lingers and hangs the process.
105
+ const ctx: APIRequestContext = await request.newContext({
106
+ baseURL: base,
107
+ extraHTTPHeaders: headers,
108
+ timeout: conf.timeout_ms ?? 15000,
109
+ ...(opts.storageState ? { storageState: opts.storageState } : {}),
110
+ });
87
111
  try {
88
- res = await fetch(url, { method: req.method, headers, body, signal: controller.signal });
112
+ const res = await ctx.fetch(urlPath, { method: req.method, ...bodyOpt });
113
+ const text = await res.text();
114
+ let parsed: any = text;
115
+ try { parsed = text ? JSON.parse(text) : null; } catch { /* non-JSON → keep text */ }
116
+ return { status: res.status(), ok: res.ok(), body: parsed, headers: res.headers() };
89
117
  } finally {
90
- clearTimeout(timer);
118
+ await ctx.dispose();
91
119
  }
92
- const text = await res.text();
93
- let parsed: any = text;
94
- try { parsed = text ? JSON.parse(text) : null; } catch { /* non-JSON → keep text */ }
95
- const outHeaders: Record<string, string> = {};
96
- res.headers.forEach((v, k) => { outHeaders[k] = v; });
97
- return { status: res.status, ok: res.ok, body: parsed, headers: outHeaders };
120
+ }
121
+
122
+ /**
123
+ * Fire the same request N times in parallel (the `@concurrent:N` primitive) and bind aggregates —
124
+ * the idempotency/race oracle. Returns the full `responses` array plus `ok_count`, `status_counts`,
125
+ * and `statuses`, asserted with `expect {{name.ok_count}} is 1` (and cross-checked against the DB via
126
+ * `@query` to prove "exactly one charge"). Path access works on the bound value: `{{name.ok_count}}`,
127
+ * `{{name.status_counts.409}}`, `{{name.responses.count}}`, `{{name.responses[0].body.id}}`.
128
+ */
129
+ async callN(
130
+ label: string,
131
+ req: { method: string; path: string; body?: unknown; encoding?: 'json' | 'form' | 'multipart'; headers?: Record<string, string>; datasource?: string },
132
+ params: Record<string, any> = {},
133
+ n = 1,
134
+ opts: { storageState?: string } = {},
135
+ ): Promise<{
136
+ responses: Array<{ status: number; ok: boolean; body: any; headers: Record<string, string> }>;
137
+ ok_count: number;
138
+ status_counts: Record<string, number>;
139
+ statuses: number[];
140
+ }> {
141
+ const count = Math.max(1, Math.floor(n));
142
+ const responses = await Promise.all(Array.from({ length: count }, () => this.call(label, req, params, opts)));
143
+ const status_counts: Record<string, number> = {};
144
+ for (const r of responses) status_counts[String(r.status)] = (status_counts[String(r.status)] || 0) + 1;
145
+ return {
146
+ responses,
147
+ ok_count: responses.filter((r) => r.ok).length,
148
+ status_counts,
149
+ statuses: responses.map((r) => r.status),
150
+ };
98
151
  }
99
152
  }
100
153
 
@@ -1,8 +1,11 @@
1
1
  declare class DataSource {
2
2
  private configs;
3
3
  private engines;
4
+ private tunnels;
4
5
  private cfg;
5
6
  private engine;
7
+ /** Close any open SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
8
+ close(): void;
6
9
  private build;
7
10
  /** A row matching `filter` must exist; if `expected` given, assert those columns on the first match. */
8
11
  assertRow(table: string, filter: Record<string, any>, expected?: Record<string, any>, datasource?: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"specs-db.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"AA+DA,cAAM,UAAU;IACd,OAAO,CAAC,OAAO,CAAiD;IAChE,OAAO,CAAC,OAAO,CAA6B;IAE5C,OAAO,CAAC,GAAG;YAQG,MAAM;IAoBpB,OAAO,CAAC,KAAK;IAOb,wGAAwG;IAClG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/H,0CAA0C;IACpC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjG,gDAAgD;IAC1C,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhH,qDAAqD;IACrD,OAAO,CAAC,MAAM;IAKd,4FAA4F;IAC5F,OAAO,CAAC,gBAAgB;IASxB;;;;OAIG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAKjG;AAMD,eAAO,MAAM,EAAE,YAAmB,CAAC"}
1
+ {"version":3,"file":"specs-db.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"AA4HA,cAAM,UAAU;IACd,OAAO,CAAC,OAAO,CAAiD;IAChE,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAoC;IAEnD,OAAO,CAAC,GAAG;YAQG,MAAM;IA6BpB,oHAAoH;IACpH,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,KAAK;IAOb,wGAAwG;IAClG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/H,0CAA0C;IACpC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjG,gDAAgD;IAC1C,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhH,qDAAqD;IACrD,OAAO,CAAC,MAAM;IAKd,4FAA4F;IAC5F,OAAO,CAAC,gBAAgB;IASxB;;;;OAIG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAKjG;AAMD,eAAO,MAAM,EAAE,YAAmB,CAAC"}
@@ -56,6 +56,65 @@ const ident = (s) => {
56
56
  throw new Error(`Unsafe identifier: ${JSON.stringify(s)} (allowed: [A-Za-z_][A-Za-z0-9_]*)`);
57
57
  return s;
58
58
  };
59
+ /**
60
+ * Open a local TCP forward (127.0.0.1:<ephemeral> → ssh bastion → dstHost:dstPort) for a DB socket.
61
+ * Sockets are unref()'d so a dangling tunnel never keeps the test process alive after the run.
62
+ */
63
+ async function openSshTunnel(ssh, dstHost, dstPort) {
64
+ const { Client } = require('ssh2');
65
+ const net = require('net');
66
+ const privateKey = ssh.private_key
67
+ ? ssh.private_key
68
+ : ssh.private_key_path
69
+ ? fs.readFileSync(ssh.private_key_path.replace(/^~(?=\/)/, process.env.HOME || ''), 'utf8')
70
+ : undefined;
71
+ if (!privateKey)
72
+ throw new Error('Data Driver: datasource `ssh` requires `private_key` or `private_key_path`.');
73
+ const conn = new Client();
74
+ await new Promise((resolve, reject) => {
75
+ conn.on('ready', resolve).on('error', reject).connect({
76
+ host: ssh.host,
77
+ port: ssh.port ?? 22,
78
+ username: ssh.user,
79
+ privateKey,
80
+ passphrase: ssh.passphrase,
81
+ hostVerifier: (key) => {
82
+ const got = Buffer.isBuffer(key) ? key.toString('base64') : String(key);
83
+ if (ssh.known_host) {
84
+ if (got === ssh.known_host.trim())
85
+ return true;
86
+ throw new Error(`Data Driver: SSH host-key mismatch for ${ssh.host} — refused (known_host pin).`);
87
+ }
88
+ console.warn(`Data Driver: SSH host key for ${ssh.host} is not pinned (set datasource ssh.known_host to verify). Proceeding (TOFU).`);
89
+ return true;
90
+ },
91
+ });
92
+ });
93
+ const server = net.createServer((sock) => {
94
+ conn.forwardOut(sock.remoteAddress || '127.0.0.1', sock.remotePort || 0, dstHost, dstPort, (err, stream) => {
95
+ if (err) {
96
+ sock.destroy();
97
+ return;
98
+ }
99
+ sock.pipe(stream).pipe(sock);
100
+ });
101
+ });
102
+ await new Promise((resolve, reject) => server.on('error', reject).listen(0, '127.0.0.1', () => resolve()));
103
+ const addr = server.address();
104
+ const port = addr && typeof addr === 'object' ? addr.port : 0;
105
+ server.unref(); // don't keep the event loop alive after tests
106
+ try {
107
+ conn._sock?.unref?.();
108
+ }
109
+ catch { /* best-effort */ }
110
+ return { host: '127.0.0.1', port, close: () => { try {
111
+ server.close();
112
+ }
113
+ catch { } try {
114
+ conn.end();
115
+ }
116
+ catch { } } };
117
+ }
59
118
  function loadEnvQa() {
60
119
  for (const name of ['.env.qa', `.env.qa.${process.env.SUNGEN_ENV || ''}`]) {
61
120
  const p = path.join(process.cwd(), name);
@@ -89,6 +148,7 @@ class DataSource {
89
148
  constructor() {
90
149
  this.configs = null;
91
150
  this.engines = new Map();
151
+ this.tunnels = [];
92
152
  }
93
153
  cfg(name) {
94
154
  if (!this.configs)
@@ -107,11 +167,22 @@ class DataSource {
107
167
  throw new Error(`Data Driver: datasource "${key}" has no url (set it in .env.qa).`);
108
168
  let engine;
109
169
  if (conf.engine === 'postgres') {
170
+ let connectionString = conf.url;
171
+ if (conf.ssh) { // Cách B: tunnel the DB socket through a bastion
172
+ const u = new URL(conf.url);
173
+ const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 5432));
174
+ this.tunnels.push(t);
175
+ u.hostname = t.host;
176
+ u.port = String(t.port); // rewrite host:port → 127.0.0.1:<tunnel> (keep user/pass/db/query)
177
+ connectionString = u.toString();
178
+ }
110
179
  const { Pool } = require('pg');
111
- const pool = new Pool({ connectionString: conf.url, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
180
+ const pool = new Pool({ connectionString, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
112
181
  engine = { query: async (sql, params) => (await pool.query(sql, params)).rows };
113
182
  }
114
183
  else if (conf.engine === 'sqlite') {
184
+ if (conf.ssh)
185
+ console.warn(`Data Driver: datasource "${key}" sets ssh: but engine is sqlite (file-based) — ssh ignored.`);
115
186
  const Database = require('better-sqlite3');
116
187
  const db = new Database(conf.url.replace(/^sqlite:/, ''), { readonly: conf.readonly !== false });
117
188
  engine = { query: async (sql, params) => db.prepare(sql).all(...params) };
@@ -122,6 +193,12 @@ class DataSource {
122
193
  this.engines.set(key, engine);
123
194
  return { engine, conf };
124
195
  }
196
+ /** Close any open SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
197
+ close() {
198
+ for (const t of this.tunnels)
199
+ t.close();
200
+ this.tunnels = [];
201
+ }
125
202
  build(table, filter) {
126
203
  const cols = Object.keys(filter);
127
204
  const where = cols.map((c, i) => `${ident(c)} = $${i + 1}`).join(' AND ');
@@ -1 +1 @@
1
- {"version":3,"file":"specs-db.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"specs-db.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"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"}
@@ -21,12 +21,73 @@ const ident = (s: string): string => {
21
21
  return s;
22
22
  };
23
23
 
24
+ interface SshConfig {
25
+ host: string; // jump host reachable from the runner
26
+ port?: number; // default 22
27
+ user: string;
28
+ private_key?: string; // PEM contents (from ${VAR} in .env.qa) — preferred for CI
29
+ private_key_path?: string; // or a filesystem path (local dev)
30
+ passphrase?: string; // for an encrypted key
31
+ known_host?: string; // base64 of the server's host key to pin (optional; else warn-and-proceed)
32
+ }
33
+
24
34
  interface DataSourceConfig {
25
35
  engine: 'postgres' | 'mysql' | 'sqlite';
26
36
  url: string;
27
37
  readonly?: boolean;
28
38
  statement_timeout_ms?: number;
29
39
  max_rows?: number;
40
+ // Cách B (fallback): tunnel the DB SOCKET through an SSH bastion. DB-only — the browser/E2E
41
+ // still run on the runner; only PG traffic crosses. See docs/spec/sungen_data_driver_ssh_tunnel_spec.md.
42
+ ssh?: SshConfig;
43
+ }
44
+
45
+ /**
46
+ * Open a local TCP forward (127.0.0.1:<ephemeral> → ssh bastion → dstHost:dstPort) for a DB socket.
47
+ * Sockets are unref()'d so a dangling tunnel never keeps the test process alive after the run.
48
+ */
49
+ async function openSshTunnel(ssh: SshConfig, dstHost: string, dstPort: number): Promise<{ host: string; port: number; close: () => void }> {
50
+ const { Client } = require('ssh2');
51
+ const net = require('net');
52
+ const privateKey = ssh.private_key
53
+ ? ssh.private_key
54
+ : ssh.private_key_path
55
+ ? fs.readFileSync(ssh.private_key_path.replace(/^~(?=\/)/, process.env.HOME || ''), 'utf8')
56
+ : undefined;
57
+ if (!privateKey) throw new Error('Data Driver: datasource `ssh` requires `private_key` or `private_key_path`.');
58
+
59
+ const conn = new Client();
60
+ await new Promise<void>((resolve, reject) => {
61
+ conn.on('ready', resolve).on('error', reject).connect({
62
+ host: ssh.host,
63
+ port: ssh.port ?? 22,
64
+ username: ssh.user,
65
+ privateKey,
66
+ passphrase: ssh.passphrase,
67
+ hostVerifier: (key: Buffer) => {
68
+ const got = Buffer.isBuffer(key) ? key.toString('base64') : String(key);
69
+ if (ssh.known_host) {
70
+ if (got === ssh.known_host.trim()) return true;
71
+ throw new Error(`Data Driver: SSH host-key mismatch for ${ssh.host} — refused (known_host pin).`);
72
+ }
73
+ console.warn(`Data Driver: SSH host key for ${ssh.host} is not pinned (set datasource ssh.known_host to verify). Proceeding (TOFU).`);
74
+ return true;
75
+ },
76
+ });
77
+ });
78
+
79
+ const server = net.createServer((sock: any) => {
80
+ conn.forwardOut(sock.remoteAddress || '127.0.0.1', sock.remotePort || 0, dstHost, dstPort, (err: any, stream: any) => {
81
+ if (err) { sock.destroy(); return; }
82
+ sock.pipe(stream).pipe(sock);
83
+ });
84
+ });
85
+ await new Promise<void>((resolve, reject) => server.on('error', reject).listen(0, '127.0.0.1', () => resolve()));
86
+ const addr = server.address();
87
+ const port = addr && typeof addr === 'object' ? addr.port : 0;
88
+ server.unref(); // don't keep the event loop alive after tests
89
+ try { (conn as any)._sock?.unref?.(); } catch { /* best-effort */ }
90
+ return { host: '127.0.0.1', port, close: () => { try { server.close(); } catch {} try { conn.end(); } catch {} } };
30
91
  }
31
92
 
32
93
  function loadEnvQa(): void {
@@ -64,6 +125,7 @@ type Engine = { query(sql: string, params: any[]): Promise<any[]>; };
64
125
  class DataSource {
65
126
  private configs: Record<string, DataSourceConfig> | null = null;
66
127
  private engines = new Map<string, Engine>();
128
+ private tunnels: Array<{ close: () => void }> = [];
67
129
 
68
130
  private cfg(name?: string): { key: string; conf: DataSourceConfig } {
69
131
  if (!this.configs) this.configs = loadConfig();
@@ -79,10 +141,19 @@ class DataSource {
79
141
  if (!conf.url) throw new Error(`Data Driver: datasource "${key}" has no url (set it in .env.qa).`);
80
142
  let engine: Engine;
81
143
  if (conf.engine === 'postgres') {
144
+ let connectionString = conf.url;
145
+ if (conf.ssh) { // Cách B: tunnel the DB socket through a bastion
146
+ const u = new URL(conf.url);
147
+ const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 5432));
148
+ this.tunnels.push(t);
149
+ u.hostname = t.host; u.port = String(t.port); // rewrite host:port → 127.0.0.1:<tunnel> (keep user/pass/db/query)
150
+ connectionString = u.toString();
151
+ }
82
152
  const { Pool } = require('pg');
83
- const pool = new Pool({ connectionString: conf.url, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
153
+ const pool = new Pool({ connectionString, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
84
154
  engine = { query: async (sql, params) => (await pool.query(sql, params)).rows };
85
155
  } else if (conf.engine === 'sqlite') {
156
+ if (conf.ssh) console.warn(`Data Driver: datasource "${key}" sets ssh: but engine is sqlite (file-based) — ssh ignored.`);
86
157
  const Database = require('better-sqlite3');
87
158
  const db = new Database(conf.url.replace(/^sqlite:/, ''), { readonly: conf.readonly !== false });
88
159
  engine = { query: async (sql, params) => db.prepare(sql).all(...params) };
@@ -93,6 +164,12 @@ class DataSource {
93
164
  return { engine, conf };
94
165
  }
95
166
 
167
+ /** Close any open SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
168
+ close(): void {
169
+ for (const t of this.tunnels) t.close();
170
+ this.tunnels = [];
171
+ }
172
+
96
173
  private build(table: string, filter: Record<string, any>): { sql: string; params: any[] } {
97
174
  const cols = Object.keys(filter);
98
175
  const where = cols.map((c, i) => `${ident(c)} = $${i + 1}`).join(' AND ');
@@ -23,7 +23,8 @@ export class TestDataLoader {
23
23
  */
24
24
  static load(screenName: string, featureName: string): TestDataLoader {
25
25
  let baseDir: string;
26
- if (screenName.startsWith('flows/')) {
26
+ if (screenName.startsWith('flows/') || screenName.startsWith('api/')) {
27
+ // flows/<flow> · api/<area> · api/flows/<flow> → qa/<screenName>/test-data
27
28
  baseDir = path.join(process.cwd(), 'qa', screenName, 'test-data');
28
29
  } else {
29
30
  baseDir = path.join(process.cwd(), 'qa', 'screens', screenName, 'test-data');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "3.1.2-beta.99",
3
+ "version": "3.2.0-beta.141",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -33,7 +33,7 @@
33
33
  "node": ">=18.0.0"
34
34
  },
35
35
  "dependencies": {
36
- "@sungen/driver-ui": "3.1.2-beta.99",
36
+ "@sungen/driver-ui": "3.2.0-beta.141",
37
37
  "@anthropic-ai/sdk": "^0.71.0",
38
38
  "@babel/parser": "^7.28.5",
39
39
  "@babel/traverse": "^7.28.5",
@@ -44,3 +44,22 @@ export interface ContextMapper {
44
44
  /** Turn the Context into generation units (+ modes), honouring the kernel's band targets. */
45
45
  decompose(ctx: Context, bands?: Record<string, number>): GenerationUnit[];
46
46
  }
47
+
48
+ /**
49
+ * Repair phase hook — a capability's deterministic fix catalog. Each rule matches a finding (audit
50
+ * gap) or a runtime failure message and proposes a concrete fix. `sungen repair` gathers the
51
+ * unit-capability's rules and turns the findings/failures into an actionable plan; the AI repair
52
+ * loop and a human get the same proposals. Drivers/projects extend the catalog by adding rules.
53
+ */
54
+ export interface RepairRule {
55
+ /** Stable id, e.g. 'api-error' | 'api-auth'. */
56
+ id: string;
57
+ /** Matches a finding or failure message. */
58
+ match: RegExp;
59
+ /** The concrete repair instruction to apply. */
60
+ fix: string;
61
+ }
62
+
63
+ export interface RepairProvider {
64
+ rules: RepairRule[];
65
+ }