@ranger-testing/ranger-cli 2.0.5 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +1 -914
- package/build/commands/addEnv.js +1 -1
- package/build/commands/authEncrypt.js +1 -36
- package/build/commands/clean.js +1 -1
- package/build/commands/config.js +1 -93
- package/build/commands/env.js +1 -98
- package/build/commands/feature.js +1 -653
- package/build/commands/hook.js +1 -33
- package/build/commands/hooks/autoPrompt.js +1 -32
- package/build/commands/hooks/disable.js +1 -33
- package/build/commands/hooks/enable.js +1 -58
- package/build/commands/hooks/exitPlanMode.js +1 -35
- package/build/commands/hooks/index.js +1 -12
- package/build/commands/hooks/output.js +1 -71
- package/build/commands/hooks/planReminder.js +1 -46
- package/build/commands/hooks/planStart.js +1 -30
- package/build/commands/hooks/postEdit.js +1 -43
- package/build/commands/hooks/preCompact.js +1 -30
- package/build/commands/hooks/sessionEnd.js +1 -25
- package/build/commands/hooks/sessionStart.js +1 -93
- package/build/commands/hooks/stopHook.js +1 -155
- package/build/commands/index.js +1 -12
- package/build/commands/login.js +1 -26
- package/build/commands/setupCi.js +1 -189
- package/build/commands/skillup.js +1 -80
- package/build/commands/start.js +1 -1
- package/build/commands/status.js +1 -198
- package/build/commands/update.js +1 -182
- package/build/commands/updateEnv.js +1 -1
- package/build/commands/useEnv.js +1 -1
- package/build/commands/utils/activeProfile.js +1 -76
- package/build/commands/utils/browserSessionsApi.js +1 -1
- package/build/commands/utils/claudeConfig.js +1 -73
- package/build/commands/utils/claudePlugin.js +1 -85
- package/build/commands/utils/crypto.js +1 -42
- package/build/commands/utils/desirePathLog.js +1 -139
- package/build/commands/utils/deviceAuth.js +1 -232
- package/build/commands/utils/environment.js +1 -65
- package/build/commands/utils/featureApi.js +1 -371
- package/build/commands/utils/featureReportGenerator.js +1 -204
- package/build/commands/utils/fixWebmDuration.js +1 -0
- package/build/commands/utils/git.js +1 -44
- package/build/commands/utils/keychain.js +1 -1
- package/build/commands/utils/localAgentInstallationsApi.js +1 -1
- package/build/commands/utils/profileMessages.js +1 -8
- package/build/commands/utils/profileSetupBanner.js +1 -167
- package/build/commands/utils/rangerRoot.js +1 -60
- package/build/commands/utils/reportGenerator.js +1 -130
- package/build/commands/utils/retry.js +1 -25
- package/build/commands/utils/sessionCache.js +1 -299
- package/build/commands/utils/settings.js +1 -313
- package/build/commands/utils/skillContent.js +1 -28
- package/build/commands/utils/skills.js +1 -1
- package/build/commands/utils/telemetry.js +1 -254
- package/build/commands/utils/userApi.js +1 -32
- package/build/commands/utils/version.js +1 -62
- package/build/commands/verifyFeature.js +1 -1343
- package/build/commands/verifyInBrowser.js +1 -1
- package/package.json +4 -1
- package/build/cli.js.map +0 -1
- package/build/commands/addEnv.js.map +0 -1
- package/build/commands/authEncrypt.js.map +0 -1
- package/build/commands/clean.js.map +0 -1
- package/build/commands/config.js.map +0 -1
- package/build/commands/env.js.map +0 -1
- package/build/commands/feature.js.map +0 -1
- package/build/commands/hook.js.map +0 -1
- package/build/commands/hooks/autoPrompt.js.map +0 -1
- package/build/commands/hooks/disable.js.map +0 -1
- package/build/commands/hooks/enable.js.map +0 -1
- package/build/commands/hooks/exitPlanMode.js.map +0 -1
- package/build/commands/hooks/index.js.map +0 -1
- package/build/commands/hooks/output.js.map +0 -1
- package/build/commands/hooks/planReminder.js.map +0 -1
- package/build/commands/hooks/planStart.js.map +0 -1
- package/build/commands/hooks/postEdit.js.map +0 -1
- package/build/commands/hooks/preCompact.js.map +0 -1
- package/build/commands/hooks/sessionEnd.js.map +0 -1
- package/build/commands/hooks/sessionStart.js.map +0 -1
- package/build/commands/hooks/stopHook.js.map +0 -1
- package/build/commands/index.js.map +0 -1
- package/build/commands/login.js.map +0 -1
- package/build/commands/setupCi.js.map +0 -1
- package/build/commands/skillup.js.map +0 -1
- package/build/commands/start.js.map +0 -1
- package/build/commands/status.js.map +0 -1
- package/build/commands/update.js.map +0 -1
- package/build/commands/updateEnv.js.map +0 -1
- package/build/commands/useEnv.js.map +0 -1
- package/build/commands/utils/activeProfile.js.map +0 -1
- package/build/commands/utils/browserSessionsApi.js.map +0 -1
- package/build/commands/utils/claudeConfig.js.map +0 -1
- package/build/commands/utils/claudePlugin.js.map +0 -1
- package/build/commands/utils/crypto.js.map +0 -1
- package/build/commands/utils/desirePathLog.js.map +0 -1
- package/build/commands/utils/deviceAuth.js.map +0 -1
- package/build/commands/utils/environment.js.map +0 -1
- package/build/commands/utils/featureApi.js.map +0 -1
- package/build/commands/utils/featureReportGenerator.js.map +0 -1
- package/build/commands/utils/git.js.map +0 -1
- package/build/commands/utils/keychain.js.map +0 -1
- package/build/commands/utils/localAgentInstallationsApi.js.map +0 -1
- package/build/commands/utils/profileMessages.js.map +0 -1
- package/build/commands/utils/profileSetupBanner.js.map +0 -1
- package/build/commands/utils/rangerRoot.js.map +0 -1
- package/build/commands/utils/reportGenerator.js.map +0 -1
- package/build/commands/utils/retry.js.map +0 -1
- package/build/commands/utils/sessionCache.js.map +0 -1
- package/build/commands/utils/settings.js.map +0 -1
- package/build/commands/utils/skillContent.js.map +0 -1
- package/build/commands/utils/skills.js.map +0 -1
- package/build/commands/utils/telemetry.js.map +0 -1
- package/build/commands/utils/userApi.js.map +0 -1
- package/build/commands/utils/version.js.map +0 -1
- package/build/commands/verifyFeature.js.map +0 -1
- package/build/commands/verifyInBrowser.js.map +0 -1
|
@@ -1,371 +1 @@
|
|
|
1
|
-
import { getToken } from './keychain.js';
|
|
2
|
-
import { getApiBaseUrl } from './environment.js';
|
|
3
|
-
import { withRetry } from './retry.js';
|
|
4
|
-
/**
|
|
5
|
-
* Create a new feature with optional scenarios
|
|
6
|
-
*/
|
|
7
|
-
export async function createFeature(data) {
|
|
8
|
-
const token = await getToken();
|
|
9
|
-
if (!token) {
|
|
10
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
11
|
-
}
|
|
12
|
-
const baseUrl = getApiBaseUrl();
|
|
13
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features`, {
|
|
14
|
-
method: 'POST',
|
|
15
|
-
headers: {
|
|
16
|
-
Authorization: `Bearer ${token}`,
|
|
17
|
-
'Content-Type': 'application/json',
|
|
18
|
-
},
|
|
19
|
-
body: JSON.stringify(data),
|
|
20
|
-
});
|
|
21
|
-
if (!response.ok) {
|
|
22
|
-
const error = await response.text();
|
|
23
|
-
throw new Error(`Failed to create feature review: ${error}`);
|
|
24
|
-
}
|
|
25
|
-
return response.json();
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* List features with optional filters
|
|
29
|
-
*/
|
|
30
|
-
export async function listFeatures(options) {
|
|
31
|
-
const token = await getToken();
|
|
32
|
-
if (!token) {
|
|
33
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
34
|
-
}
|
|
35
|
-
const baseUrl = getApiBaseUrl();
|
|
36
|
-
const params = new URLSearchParams();
|
|
37
|
-
if (options?.gitRepoUrl)
|
|
38
|
-
params.set('gitRepoUrl', options.gitRepoUrl);
|
|
39
|
-
if (options?.gitBranch)
|
|
40
|
-
params.set('gitBranch', options.gitBranch);
|
|
41
|
-
if (options?.limit)
|
|
42
|
-
params.set('limit', options.limit.toString());
|
|
43
|
-
if (options?.offset)
|
|
44
|
-
params.set('offset', options.offset.toString());
|
|
45
|
-
if (options?.includeDeleted)
|
|
46
|
-
params.set('includeDeleted', 'true');
|
|
47
|
-
const url = `${baseUrl}/api/v1/mcp/features${params.toString() ? `?${params.toString()}` : ''}`;
|
|
48
|
-
const response = await fetch(url, {
|
|
49
|
-
method: 'GET',
|
|
50
|
-
headers: {
|
|
51
|
-
Authorization: `Bearer ${token}`,
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
if (!response.ok) {
|
|
55
|
-
const error = await response.text();
|
|
56
|
-
throw new Error(`Failed to list feature reviews: ${error}`);
|
|
57
|
-
}
|
|
58
|
-
return response.json();
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Get a feature by ID with scenarios and sessions
|
|
62
|
-
*/
|
|
63
|
-
export async function getFeature(id) {
|
|
64
|
-
const token = await getToken();
|
|
65
|
-
if (!token) {
|
|
66
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
67
|
-
}
|
|
68
|
-
const baseUrl = getApiBaseUrl();
|
|
69
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${id}`, {
|
|
70
|
-
method: 'GET',
|
|
71
|
-
headers: {
|
|
72
|
-
Authorization: `Bearer ${token}`,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
const error = await response.text();
|
|
77
|
-
throw new Error(`Failed to get feature review: ${error}`);
|
|
78
|
-
}
|
|
79
|
-
return response.json();
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Update a feature
|
|
83
|
-
*/
|
|
84
|
-
export async function updateFeature(id, updates) {
|
|
85
|
-
const token = await getToken();
|
|
86
|
-
if (!token) {
|
|
87
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
88
|
-
}
|
|
89
|
-
const baseUrl = getApiBaseUrl();
|
|
90
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${id}`, {
|
|
91
|
-
method: 'PATCH',
|
|
92
|
-
headers: {
|
|
93
|
-
Authorization: `Bearer ${token}`,
|
|
94
|
-
'Content-Type': 'application/json',
|
|
95
|
-
},
|
|
96
|
-
body: JSON.stringify(updates),
|
|
97
|
-
});
|
|
98
|
-
if (!response.ok) {
|
|
99
|
-
const error = await response.text();
|
|
100
|
-
throw new Error(`Failed to update feature review: ${error}`);
|
|
101
|
-
}
|
|
102
|
-
return response.json();
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Create a new scenario
|
|
106
|
-
*/
|
|
107
|
-
export async function createChecklistItem(featureId, description, position) {
|
|
108
|
-
const token = await getToken();
|
|
109
|
-
if (!token) {
|
|
110
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
111
|
-
}
|
|
112
|
-
const baseUrl = getApiBaseUrl();
|
|
113
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/checklist`, {
|
|
114
|
-
method: 'POST',
|
|
115
|
-
headers: {
|
|
116
|
-
Authorization: `Bearer ${token}`,
|
|
117
|
-
'Content-Type': 'application/json',
|
|
118
|
-
},
|
|
119
|
-
body: JSON.stringify({ description, position }),
|
|
120
|
-
});
|
|
121
|
-
if (!response.ok) {
|
|
122
|
-
const error = await response.text();
|
|
123
|
-
throw new Error(`Failed to create scenario: ${error}`);
|
|
124
|
-
}
|
|
125
|
-
return response.json();
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Update a scenario
|
|
129
|
-
*/
|
|
130
|
-
export async function updateChecklistItem(featureId, itemId, updates) {
|
|
131
|
-
return withRetry(async () => {
|
|
132
|
-
const token = await getToken();
|
|
133
|
-
if (!token) {
|
|
134
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
135
|
-
}
|
|
136
|
-
const baseUrl = getApiBaseUrl();
|
|
137
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/checklist/${itemId}`, {
|
|
138
|
-
method: 'PATCH',
|
|
139
|
-
headers: {
|
|
140
|
-
Authorization: `Bearer ${token}`,
|
|
141
|
-
'Content-Type': 'application/json',
|
|
142
|
-
},
|
|
143
|
-
body: JSON.stringify(updates),
|
|
144
|
-
});
|
|
145
|
-
if (!response.ok) {
|
|
146
|
-
const error = await response.text();
|
|
147
|
-
throw new Error(`Failed to update scenario: ${error}`);
|
|
148
|
-
}
|
|
149
|
-
return response.json();
|
|
150
|
-
}, { label: 'updateChecklistItem' });
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Get feature report data
|
|
154
|
-
*/
|
|
155
|
-
export async function getFeatureReport(id) {
|
|
156
|
-
const token = await getToken();
|
|
157
|
-
if (!token) {
|
|
158
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
159
|
-
}
|
|
160
|
-
const baseUrl = getApiBaseUrl();
|
|
161
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${id}/report`, {
|
|
162
|
-
method: 'GET',
|
|
163
|
-
headers: {
|
|
164
|
-
Authorization: `Bearer ${token}`,
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
if (!response.ok) {
|
|
168
|
-
const error = await response.text();
|
|
169
|
-
throw new Error(`Failed to get feature review report: ${error}`);
|
|
170
|
-
}
|
|
171
|
-
return response.json();
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Get feature report as markdown with embedded screenshots
|
|
175
|
-
*/
|
|
176
|
-
export async function getFeatureReportMarkdown(id, options) {
|
|
177
|
-
const token = await getToken();
|
|
178
|
-
if (!token) {
|
|
179
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
180
|
-
}
|
|
181
|
-
const baseUrl = getApiBaseUrl();
|
|
182
|
-
const params = new URLSearchParams();
|
|
183
|
-
if (options?.style) {
|
|
184
|
-
params.set('style', options.style);
|
|
185
|
-
}
|
|
186
|
-
const url = `${baseUrl}/api/v1/mcp/features/${id}/report/markdown${params.toString() ? `?${params.toString()}` : ''}`;
|
|
187
|
-
const response = await fetch(url, {
|
|
188
|
-
method: 'GET',
|
|
189
|
-
headers: {
|
|
190
|
-
Authorization: `Bearer ${token}`,
|
|
191
|
-
},
|
|
192
|
-
});
|
|
193
|
-
if (!response.ok) {
|
|
194
|
-
const error = await response.text();
|
|
195
|
-
throw new Error(`Failed to get feature review report markdown: ${error}`);
|
|
196
|
-
}
|
|
197
|
-
return response.text();
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* List sessions for a feature
|
|
201
|
-
*/
|
|
202
|
-
export async function listFeatureSessions(featureId) {
|
|
203
|
-
const token = await getToken();
|
|
204
|
-
if (!token) {
|
|
205
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
206
|
-
}
|
|
207
|
-
const baseUrl = getApiBaseUrl();
|
|
208
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/sessions`, {
|
|
209
|
-
method: 'GET',
|
|
210
|
-
headers: {
|
|
211
|
-
Authorization: `Bearer ${token}`,
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
if (!response.ok) {
|
|
215
|
-
const error = await response.text();
|
|
216
|
-
throw new Error(`Failed to list sessions: ${error}`);
|
|
217
|
-
}
|
|
218
|
-
return response.json();
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Start a session (changes status from ready to in_progress)
|
|
222
|
-
*/
|
|
223
|
-
export async function startSession(featureId, sessionId) {
|
|
224
|
-
const token = await getToken();
|
|
225
|
-
if (!token) {
|
|
226
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
227
|
-
}
|
|
228
|
-
const baseUrl = getApiBaseUrl();
|
|
229
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/sessions/${sessionId}/start`, {
|
|
230
|
-
method: 'POST',
|
|
231
|
-
headers: {
|
|
232
|
-
Authorization: `Bearer ${token}`,
|
|
233
|
-
},
|
|
234
|
-
});
|
|
235
|
-
if (!response.ok) {
|
|
236
|
-
const error = await response.text();
|
|
237
|
-
throw new Error(`Failed to start session: ${error}`);
|
|
238
|
-
}
|
|
239
|
-
return response.json();
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Conclude a session (end even with incomplete items)
|
|
243
|
-
*/
|
|
244
|
-
export async function concludeSession(featureId, sessionId) {
|
|
245
|
-
const token = await getToken();
|
|
246
|
-
if (!token) {
|
|
247
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
248
|
-
}
|
|
249
|
-
const baseUrl = getApiBaseUrl();
|
|
250
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/sessions/${sessionId}/conclude`, {
|
|
251
|
-
method: 'POST',
|
|
252
|
-
headers: {
|
|
253
|
-
Authorization: `Bearer ${token}`,
|
|
254
|
-
},
|
|
255
|
-
});
|
|
256
|
-
if (!response.ok) {
|
|
257
|
-
const error = await response.text();
|
|
258
|
-
throw new Error(`Failed to conclude session: ${error}`);
|
|
259
|
-
}
|
|
260
|
-
return response.json();
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Get action items for a feature - leaf items that can be verified.
|
|
264
|
-
* Returns non-closed items with no non-closed children, plus unaddressed comment counts.
|
|
265
|
-
*/
|
|
266
|
-
export async function getActionItems(featureId) {
|
|
267
|
-
const token = await getToken();
|
|
268
|
-
if (!token) {
|
|
269
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
270
|
-
}
|
|
271
|
-
const baseUrl = getApiBaseUrl();
|
|
272
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/action-items`, {
|
|
273
|
-
method: 'GET',
|
|
274
|
-
headers: {
|
|
275
|
-
Authorization: `Bearer ${token}`,
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
if (!response.ok) {
|
|
279
|
-
const error = await response.text();
|
|
280
|
-
throw new Error(`Failed to get action items: ${error}`);
|
|
281
|
-
}
|
|
282
|
-
return response.json();
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Get feedback and verification instructions for a scenario.
|
|
286
|
-
* Returns unaddressed comments, parent description, and canonical flow.
|
|
287
|
-
*/
|
|
288
|
-
export async function getItemFeedback(featureId, itemId) {
|
|
289
|
-
const token = await getToken();
|
|
290
|
-
if (!token) {
|
|
291
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
292
|
-
}
|
|
293
|
-
const baseUrl = getApiBaseUrl();
|
|
294
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/checklist-items/${itemId}/feedback`, {
|
|
295
|
-
method: 'GET',
|
|
296
|
-
headers: {
|
|
297
|
-
Authorization: `Bearer ${token}`,
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
if (!response.ok) {
|
|
301
|
-
const error = await response.text();
|
|
302
|
-
throw new Error(`Failed to get item feedback: ${error}`);
|
|
303
|
-
}
|
|
304
|
-
return response.json();
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Mark specific comments as addressed by a checklist item.
|
|
308
|
-
*/
|
|
309
|
-
export async function markCommentsAddressed(featureId, checklistItemId, commentIds) {
|
|
310
|
-
const token = await getToken();
|
|
311
|
-
if (!token) {
|
|
312
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
313
|
-
}
|
|
314
|
-
const baseUrl = getApiBaseUrl();
|
|
315
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${featureId}/comments/address`, {
|
|
316
|
-
method: 'POST',
|
|
317
|
-
headers: {
|
|
318
|
-
Authorization: `Bearer ${token}`,
|
|
319
|
-
'Content-Type': 'application/json',
|
|
320
|
-
},
|
|
321
|
-
body: JSON.stringify({
|
|
322
|
-
checklistItemId,
|
|
323
|
-
commentIds,
|
|
324
|
-
}),
|
|
325
|
-
});
|
|
326
|
-
if (!response.ok) {
|
|
327
|
-
const error = await response.text();
|
|
328
|
-
throw new Error(`Failed to mark comments as addressed: ${error}`);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Soft delete a feature
|
|
333
|
-
*/
|
|
334
|
-
export async function softDeleteFeature(id) {
|
|
335
|
-
const token = await getToken();
|
|
336
|
-
if (!token) {
|
|
337
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
338
|
-
}
|
|
339
|
-
const baseUrl = getApiBaseUrl();
|
|
340
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${id}`, {
|
|
341
|
-
method: 'DELETE',
|
|
342
|
-
headers: {
|
|
343
|
-
Authorization: `Bearer ${token}`,
|
|
344
|
-
},
|
|
345
|
-
});
|
|
346
|
-
if (!response.ok) {
|
|
347
|
-
const error = await response.text();
|
|
348
|
-
throw new Error(`Failed to delete feature review: ${error}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Restore a soft-deleted feature
|
|
353
|
-
*/
|
|
354
|
-
export async function restoreFeature(id) {
|
|
355
|
-
const token = await getToken();
|
|
356
|
-
if (!token) {
|
|
357
|
-
throw new Error('No API token configured. Run: ranger setup [token]');
|
|
358
|
-
}
|
|
359
|
-
const baseUrl = getApiBaseUrl();
|
|
360
|
-
const response = await fetch(`${baseUrl}/api/v1/mcp/features/${id}/restore`, {
|
|
361
|
-
method: 'POST',
|
|
362
|
-
headers: {
|
|
363
|
-
Authorization: `Bearer ${token}`,
|
|
364
|
-
},
|
|
365
|
-
});
|
|
366
|
-
if (!response.ok) {
|
|
367
|
-
const error = await response.text();
|
|
368
|
-
throw new Error(`Failed to restore feature review: ${error}`);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
//# sourceMappingURL=featureApi.js.map
|
|
1
|
+
(function(_0x490063,_0x119f31){const _0x37834e=_0x5026,_0x111e73=_0x490063();while(!![]){try{const _0x4d4c4a=-parseInt(_0x37834e(0x1f9))/0x1+-parseInt(_0x37834e(0x1fa))/0x2*(parseInt(_0x37834e(0x208))/0x3)+-parseInt(_0x37834e(0x1f2))/0x4*(-parseInt(_0x37834e(0x1e5))/0x5)+-parseInt(_0x37834e(0x204))/0x6*(parseInt(_0x37834e(0x1fb))/0x7)+parseInt(_0x37834e(0x1e4))/0x8+-parseInt(_0x37834e(0x20b))/0x9+parseInt(_0x37834e(0x1f1))/0xa;if(_0x4d4c4a===_0x119f31)break;else _0x111e73['push'](_0x111e73['shift']());}catch(_0x1685cd){_0x111e73['push'](_0x111e73['shift']());}}}(_0x94b4,0xe7932));import{getToken}from'./keychain.js';import{getApiBaseUrl}from'./environment.js';import{withRetry}from'./retry.js';export async function createFeature(_0x32f541){const _0x295afd=_0x5026,_0x40c009={'gLchK':'No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]','NRhlm':function(_0x19200c,_0x17c9f1,_0x11e70a){return _0x19200c(_0x17c9f1,_0x11e70a);}},_0x5f2a49=await getToken();if(!_0x5f2a49)throw new Error(_0x40c009[_0x295afd(0x200)]);const _0xfa8e2=getApiBaseUrl(),_0x256fb4=await _0x40c009[_0x295afd(0x1e2)](fetch,_0xfa8e2+'/api/v1/mcp/features',{'method':'POST','headers':{'Authorization':'Bearer\x20'+_0x5f2a49,'Content-Type':'application/json'},'body':JSON['stringify'](_0x32f541)});if(!_0x256fb4['ok']){const _0x462c76=await _0x256fb4[_0x295afd(0x1cc)]();throw new Error('Failed\x20to\x20create\x20feature\x20review:\x20'+_0x462c76);}return _0x256fb4['json']();}export async function listFeatures(_0xf6a3b6){const _0x36fc68=_0x5026,_0x47d7bc={'eeStY':function(_0x1c34c5){return _0x1c34c5();},'PShhD':_0x36fc68(0x1df),'NOlHf':'true','zSone':_0x36fc68(0x1e9)},_0x11d8a1=await getToken();if(!_0x11d8a1)throw new Error(_0x36fc68(0x1dc));const _0x1225ab=_0x47d7bc['eeStY'](getApiBaseUrl),_0x1124ec=new URLSearchParams();if(_0xf6a3b6?.[_0x36fc68(0x202)])_0x1124ec['set']('gitRepoUrl',_0xf6a3b6[_0x36fc68(0x202)]);if(_0xf6a3b6?.[_0x36fc68(0x1ff)])_0x1124ec[_0x36fc68(0x209)]('gitBranch',_0xf6a3b6[_0x36fc68(0x1ff)]);if(_0xf6a3b6?.[_0x36fc68(0x1d3)])_0x1124ec[_0x36fc68(0x209)]('limit',_0xf6a3b6['limit']['toString']());if(_0xf6a3b6?.['offset'])_0x1124ec[_0x36fc68(0x209)]('offset',_0xf6a3b6[_0x36fc68(0x205)]['toString']());if(_0xf6a3b6?.['includeDeleted'])_0x1124ec[_0x36fc68(0x209)](_0x47d7bc['PShhD'],_0x47d7bc['NOlHf']);const _0x1a2a84=_0x1225ab+_0x36fc68(0x1f0)+(_0x1124ec[_0x36fc68(0x20e)]()?'?'+_0x1124ec[_0x36fc68(0x20e)]():''),_0x131604=await fetch(_0x1a2a84,{'method':_0x47d7bc[_0x36fc68(0x1f3)],'headers':{'Authorization':'Bearer\x20'+_0x11d8a1}});if(!_0x131604['ok']){const _0x636d2a=await _0x131604[_0x36fc68(0x1cc)]();throw new Error('Failed\x20to\x20list\x20feature\x20reviews:\x20'+_0x636d2a);}return _0x131604[_0x36fc68(0x203)]();}export async function getFeature(_0x2502d0){const _0x3443e1=_0x5026,_0x3bca1d={'zWXzW':function(_0x58e254){return _0x58e254();},'ZvhHk':function(_0x190ccf,_0x337e8d,_0x1f5a44){return _0x190ccf(_0x337e8d,_0x1f5a44);}},_0x588978=await getToken();if(!_0x588978)throw new Error(_0x3443e1(0x1dc));const _0x1ee60a=_0x3bca1d[_0x3443e1(0x1f4)](getApiBaseUrl),_0x2e9e27=await _0x3bca1d['ZvhHk'](fetch,_0x1ee60a+_0x3443e1(0x1d5)+_0x2502d0,{'method':_0x3443e1(0x1e9),'headers':{'Authorization':_0x3443e1(0x1da)+_0x588978}});if(!_0x2e9e27['ok']){const _0x4512f0=await _0x2e9e27[_0x3443e1(0x1cc)]();throw new Error(_0x3443e1(0x1d2)+_0x4512f0);}return _0x2e9e27['json']();}export async function updateFeature(_0x8034d1,_0x5850c4){const _0x555056=_0x5026,_0x35c333={'BbMJz':function(_0x300a0a){return _0x300a0a();},'uIcEI':function(_0x188b11,_0x3e8cab,_0x3467b2){return _0x188b11(_0x3e8cab,_0x3467b2);},'phcaP':_0x555056(0x1d6)},_0x238e30=await _0x35c333[_0x555056(0x1e1)](getToken);if(!_0x238e30)throw new Error('No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]');const _0x274d62=getApiBaseUrl(),_0x1b8e5f=await _0x35c333['uIcEI'](fetch,_0x274d62+'/api/v1/mcp/features/'+_0x8034d1,{'method':_0x555056(0x1de),'headers':{'Authorization':_0x555056(0x1da)+_0x238e30,'Content-Type':_0x35c333[_0x555056(0x1d4)]},'body':JSON[_0x555056(0x1cf)](_0x5850c4)});if(!_0x1b8e5f['ok']){const _0x55d434=await _0x1b8e5f[_0x555056(0x1cc)]();throw new Error(_0x555056(0x206)+_0x55d434);}return _0x1b8e5f[_0x555056(0x203)]();}export async function createChecklistItem(_0x58853a,_0x136fd8,_0x59d1f3){const _0x204666=_0x5026,_0x9199dc={'vtENp':function(_0x24ac3c){return _0x24ac3c();},'BRnkh':'No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]','DkscR':function(_0x2f7521){return _0x2f7521();},'gFalO':_0x204666(0x1f7),'CwpCf':_0x204666(0x1d6)},_0x4e5495=await _0x9199dc['vtENp'](getToken);if(!_0x4e5495)throw new Error(_0x9199dc[_0x204666(0x1e6)]);const _0x29931a=_0x9199dc['DkscR'](getApiBaseUrl),_0x859d80=await fetch(_0x29931a+'/api/v1/mcp/features/'+_0x58853a+_0x204666(0x1db),{'method':_0x9199dc[_0x204666(0x1ec)],'headers':{'Authorization':_0x204666(0x1da)+_0x4e5495,'Content-Type':_0x9199dc[_0x204666(0x1d8)]},'body':JSON['stringify']({'description':_0x136fd8,'position':_0x59d1f3})});if(!_0x859d80['ok']){const _0x2fb35d=await _0x859d80[_0x204666(0x1cc)]();throw new Error(_0x204666(0x1ed)+_0x2fb35d);}return _0x859d80[_0x204666(0x203)]();}export async function updateChecklistItem(_0x2dfed4,_0x4e9d7d,_0x27de94){const _0x3d1415=_0x5026,_0xe2792={'XcPwW':'No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]','LSKGK':function(_0x4ca0df){return _0x4ca0df();},'OJREx':function(_0x44e56c,_0x12aa7f,_0x575419){return _0x44e56c(_0x12aa7f,_0x575419);},'NgXlS':_0x3d1415(0x1de)};return withRetry(async()=>{const _0x2cf4e9=_0x3d1415,_0x4681f1=await getToken();if(!_0x4681f1)throw new Error(_0xe2792['XcPwW']);const _0x4c963c=_0xe2792[_0x2cf4e9(0x20f)](getApiBaseUrl),_0x1d57ef=await _0xe2792[_0x2cf4e9(0x1f8)](fetch,_0x4c963c+'/api/v1/mcp/features/'+_0x2dfed4+'/checklist/'+_0x4e9d7d,{'method':_0xe2792[_0x2cf4e9(0x201)],'headers':{'Authorization':'Bearer\x20'+_0x4681f1,'Content-Type':_0x2cf4e9(0x1d6)},'body':JSON[_0x2cf4e9(0x1cf)](_0x27de94)});if(!_0x1d57ef['ok']){const _0x5b7016=await _0x1d57ef[_0x2cf4e9(0x1cc)]();throw new Error(_0x2cf4e9(0x1fe)+_0x5b7016);}return _0x1d57ef[_0x2cf4e9(0x203)]();},{'label':'updateChecklistItem'});}export async function getFeatureReport(_0x19c61b){const _0x207c09=_0x5026,_0x2caecd={'eXleP':function(_0x5e6c02){return _0x5e6c02();},'taroL':_0x207c09(0x1dc),'AzuuI':function(_0x36a81a){return _0x36a81a();},'WAZzt':function(_0x57c04a,_0x174ea6,_0x1ec894){return _0x57c04a(_0x174ea6,_0x1ec894);},'WJArB':'GET'},_0x596972=await _0x2caecd[_0x207c09(0x1fc)](getToken);if(!_0x596972)throw new Error(_0x2caecd[_0x207c09(0x207)]);const _0x48f543=_0x2caecd['AzuuI'](getApiBaseUrl),_0x214204=await _0x2caecd['WAZzt'](fetch,_0x48f543+'/api/v1/mcp/features/'+_0x19c61b+'/report',{'method':_0x2caecd[_0x207c09(0x1ce)],'headers':{'Authorization':_0x207c09(0x1da)+_0x596972}});if(!_0x214204['ok']){const _0x307a46=await _0x214204['text']();throw new Error(_0x207c09(0x1dd)+_0x307a46);}return _0x214204['json']();}export async function getFeatureReportMarkdown(_0x1c2e19,_0x477178){const _0x384a62=_0x5026,_0x570e6a={'xSwiz':function(_0x1619fe){return _0x1619fe();},'AzSdh':'style'},_0x335c49=await _0x570e6a['xSwiz'](getToken);if(!_0x335c49)throw new Error(_0x384a62(0x1dc));const _0x23181e=_0x570e6a['xSwiz'](getApiBaseUrl),_0x3d904a=new URLSearchParams();_0x477178?.['style']&&_0x3d904a['set'](_0x570e6a[_0x384a62(0x1d9)],_0x477178[_0x384a62(0x1f6)]);const _0x494185=_0x23181e+'/api/v1/mcp/features/'+_0x1c2e19+_0x384a62(0x20c)+(_0x3d904a[_0x384a62(0x20e)]()?'?'+_0x3d904a['toString']():''),_0x3ab4fe=await fetch(_0x494185,{'method':'GET','headers':{'Authorization':'Bearer\x20'+_0x335c49}});if(!_0x3ab4fe['ok']){const _0x1af5d=await _0x3ab4fe['text']();throw new Error('Failed\x20to\x20get\x20feature\x20review\x20report\x20markdown:\x20'+_0x1af5d);}return _0x3ab4fe['text']();}function _0x94b4(){const _0x21fb3b=['mHFDF','Failed\x20to\x20get\x20feature\x20review:\x20','limit','phcaP','/api/v1/mcp/features/','application/json','NFZQP','CwpCf','AzSdh','Bearer\x20','/checklist','No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]','Failed\x20to\x20get\x20feature\x20review\x20report:\x20','PATCH','includeDeleted','/action-items','BbMJz','NRhlm','DELETE','10727528JjVmNQ','125PEGeVo','BRnkh','Failed\x20to\x20restore\x20feature\x20review:\x20','CNYtw','GET','Failed\x20to\x20mark\x20comments\x20as\x20addressed:\x20','OXObg','gFalO','Failed\x20to\x20create\x20scenario:\x20','/feedback','/conclude','/api/v1/mcp/features','17146280XBswFr','195764NhWOpP','zSone','zWXzW','QHgKp','style','POST','OJREx','1453952PyNNQh','2205964jHjeQO','944657PMcaZq','eXleP','ygJBb','Failed\x20to\x20update\x20scenario:\x20','gitBranch','gLchK','NgXlS','gitRepoUrl','json','6dVCtSm','offset','Failed\x20to\x20update\x20feature\x20review:\x20','taroL','3YnOIXZ','set','/sessions/','5748111GRSfLf','/report/markdown','/restore','toString','LSKGK','/checklist-items/','text','Failed\x20to\x20get\x20action\x20items:\x20','WJArB','stringify','YNGXa'];_0x94b4=function(){return _0x21fb3b;};return _0x94b4();}export async function listFeatureSessions(_0x5ab323){const _0x36f514=_0x5026,_0x272c46={'ygJBb':function(_0x48a20a,_0x137d74,_0x5a3203){return _0x48a20a(_0x137d74,_0x5a3203);},'BgDuu':_0x36f514(0x1e9)},_0x20dc7e=await getToken();if(!_0x20dc7e)throw new Error('No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]');const _0x5bafb1=getApiBaseUrl(),_0xd29b6f=await _0x272c46[_0x36f514(0x1fd)](fetch,_0x5bafb1+'/api/v1/mcp/features/'+_0x5ab323+'/sessions',{'method':_0x272c46['BgDuu'],'headers':{'Authorization':_0x36f514(0x1da)+_0x20dc7e}});if(!_0xd29b6f['ok']){const _0x54a71b=await _0xd29b6f[_0x36f514(0x1cc)]();throw new Error('Failed\x20to\x20list\x20sessions:\x20'+_0x54a71b);}return _0xd29b6f[_0x36f514(0x203)]();}export async function startSession(_0x6eaf5d,_0x4e0bf1){const _0x52fba6=_0x5026,_0x305ee9={'OXObg':_0x52fba6(0x1dc),'NFZQP':'POST'},_0x1050f2=await getToken();if(!_0x1050f2)throw new Error(_0x305ee9[_0x52fba6(0x1eb)]);const _0x389bbd=getApiBaseUrl(),_0x1e1c98=await fetch(_0x389bbd+_0x52fba6(0x1d5)+_0x6eaf5d+'/sessions/'+_0x4e0bf1+'/start',{'method':_0x305ee9[_0x52fba6(0x1d7)],'headers':{'Authorization':'Bearer\x20'+_0x1050f2}});if(!_0x1e1c98['ok']){const _0x1f16b9=await _0x1e1c98['text']();throw new Error('Failed\x20to\x20start\x20session:\x20'+_0x1f16b9);}return _0x1e1c98[_0x52fba6(0x203)]();}export async function concludeSession(_0x2e62a8,_0x2f33c5){const _0x2d9548=_0x5026,_0x55209c={'dYiRP':_0x2d9548(0x1dc),'lyEAh':'POST'},_0x29922b=await getToken();if(!_0x29922b)throw new Error(_0x55209c['dYiRP']);const _0x3e9301=getApiBaseUrl(),_0x59d45e=await fetch(_0x3e9301+_0x2d9548(0x1d5)+_0x2e62a8+_0x2d9548(0x20a)+_0x2f33c5+_0x2d9548(0x1ef),{'method':_0x55209c['lyEAh'],'headers':{'Authorization':'Bearer\x20'+_0x29922b}});if(!_0x59d45e['ok']){const _0x222682=await _0x59d45e[_0x2d9548(0x1cc)]();throw new Error('Failed\x20to\x20conclude\x20session:\x20'+_0x222682);}return _0x59d45e[_0x2d9548(0x203)]();}export async function getActionItems(_0x45346c){const _0x42a559=_0x5026,_0x36c32f={'dhWyy':'No\x20API\x20token\x20configured.\x20Run:\x20ranger\x20setup\x20[token]','XwcOy':function(_0x147e0a){return _0x147e0a();},'QHgKp':'GET'},_0x14e4c6=await getToken();if(!_0x14e4c6)throw new Error(_0x36c32f['dhWyy']);const _0xab7d70=_0x36c32f['XwcOy'](getApiBaseUrl),_0x2eac48=await fetch(_0xab7d70+'/api/v1/mcp/features/'+_0x45346c+_0x42a559(0x1e0),{'method':_0x36c32f[_0x42a559(0x1f5)],'headers':{'Authorization':'Bearer\x20'+_0x14e4c6}});if(!_0x2eac48['ok']){const _0x5a6c80=await _0x2eac48[_0x42a559(0x1cc)]();throw new Error(_0x42a559(0x1cd)+_0x5a6c80);}return _0x2eac48[_0x42a559(0x203)]();}function _0x5026(_0x17a471,_0x49e830){_0x17a471=_0x17a471-0x1cc;const _0x94b41c=_0x94b4();let _0x502630=_0x94b41c[_0x17a471];return _0x502630;}export async function getItemFeedback(_0x581420,_0x352e90){const _0x1adfbc=_0x5026,_0x265add={'iUdwL':function(_0x2512be){return _0x2512be();},'mHFDF':_0x1adfbc(0x1dc),'UetHc':function(_0xc81cf5){return _0xc81cf5();}},_0x3c3034=await _0x265add['iUdwL'](getToken);if(!_0x3c3034)throw new Error(_0x265add[_0x1adfbc(0x1d1)]);const _0x11d671=_0x265add['UetHc'](getApiBaseUrl),_0x4df81e=await fetch(_0x11d671+_0x1adfbc(0x1d5)+_0x581420+_0x1adfbc(0x210)+_0x352e90+_0x1adfbc(0x1ee),{'method':'GET','headers':{'Authorization':'Bearer\x20'+_0x3c3034}});if(!_0x4df81e['ok']){const _0x3b2ced=await _0x4df81e[_0x1adfbc(0x1cc)]();throw new Error('Failed\x20to\x20get\x20item\x20feedback:\x20'+_0x3b2ced);}return _0x4df81e['json']();}export async function markCommentsAddressed(_0x44753c,_0x53620c,_0x13bf57){const _0x1292f1=_0x5026,_0x4dda12={'mfkTa':function(_0x2b5872){return _0x2b5872();}},_0x5d0bfe=await getToken();if(!_0x5d0bfe)throw new Error(_0x1292f1(0x1dc));const _0x4be475=_0x4dda12['mfkTa'](getApiBaseUrl),_0x25f9a4=await fetch(_0x4be475+_0x1292f1(0x1d5)+_0x44753c+'/comments/address',{'method':_0x1292f1(0x1f7),'headers':{'Authorization':'Bearer\x20'+_0x5d0bfe,'Content-Type':_0x1292f1(0x1d6)},'body':JSON[_0x1292f1(0x1cf)]({'checklistItemId':_0x53620c,'commentIds':_0x13bf57})});if(!_0x25f9a4['ok']){const _0x192c65=await _0x25f9a4[_0x1292f1(0x1cc)]();throw new Error(_0x1292f1(0x1ea)+_0x192c65);}}export async function softDeleteFeature(_0x344579){const _0x5d3128=_0x5026,_0x356d51=await getToken();if(!_0x356d51)throw new Error(_0x5d3128(0x1dc));const _0x24f122=getApiBaseUrl(),_0x571698=await fetch(_0x24f122+'/api/v1/mcp/features/'+_0x344579,{'method':_0x5d3128(0x1e3),'headers':{'Authorization':_0x5d3128(0x1da)+_0x356d51}});if(!_0x571698['ok']){const _0x492cd9=await _0x571698['text']();throw new Error('Failed\x20to\x20delete\x20feature\x20review:\x20'+_0x492cd9);}}export async function restoreFeature(_0x13d808){const _0x554b3b=_0x5026,_0x5b0fb0={'CNYtw':function(_0x240103){return _0x240103();},'loqBb':_0x554b3b(0x1dc),'wKyJl':function(_0x37f968,_0x57e76b,_0x3fb290){return _0x37f968(_0x57e76b,_0x3fb290);},'YNGXa':'POST'},_0x35aa93=await _0x5b0fb0[_0x554b3b(0x1e8)](getToken);if(!_0x35aa93)throw new Error(_0x5b0fb0['loqBb']);const _0x376973=_0x5b0fb0[_0x554b3b(0x1e8)](getApiBaseUrl),_0x1b4a90=await _0x5b0fb0['wKyJl'](fetch,_0x376973+_0x554b3b(0x1d5)+_0x13d808+_0x554b3b(0x20d),{'method':_0x5b0fb0[_0x554b3b(0x1d0)],'headers':{'Authorization':_0x554b3b(0x1da)+_0x35aa93}});if(!_0x1b4a90['ok']){const _0x12196b=await _0x1b4a90[_0x554b3b(0x1cc)]();throw new Error(_0x554b3b(0x1e7)+_0x12196b);}}
|
|
@@ -1,204 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Build the Playwright trace viewer URL
|
|
3
|
-
*/
|
|
4
|
-
export function buildTraceViewerUrl(traceUrl) {
|
|
5
|
-
const encodedUrl = encodeURIComponent(traceUrl);
|
|
6
|
-
return `https://trace.playwright.dev/?trace=${encodedUrl}`;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Get status emoji
|
|
10
|
-
*/
|
|
11
|
-
function getStatusEmoji(status) {
|
|
12
|
-
switch (status) {
|
|
13
|
-
case 'verified':
|
|
14
|
-
case 'completed':
|
|
15
|
-
return '\u2705'; // green check
|
|
16
|
-
case 'pending':
|
|
17
|
-
case 'in_progress':
|
|
18
|
-
return '\ud83d\udd04'; // rotating arrows
|
|
19
|
-
case 'incomplete':
|
|
20
|
-
return '\ud83d\udfe0'; // orange circle 🟠
|
|
21
|
-
case 'blocked':
|
|
22
|
-
return '\ud83d\uded1'; // stop sign
|
|
23
|
-
case 'closed':
|
|
24
|
-
return '\u26d4'; // no entry
|
|
25
|
-
default:
|
|
26
|
-
return '\u2753'; // question mark
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Format duration in human-readable form
|
|
31
|
-
*/
|
|
32
|
-
function formatDuration(ms) {
|
|
33
|
-
if (ms === null || ms === undefined)
|
|
34
|
-
return 'N/A';
|
|
35
|
-
if (ms < 1000)
|
|
36
|
-
return `${ms}ms`;
|
|
37
|
-
const seconds = Math.floor(ms / 1000);
|
|
38
|
-
if (seconds < 60)
|
|
39
|
-
return `${seconds}s`;
|
|
40
|
-
const minutes = Math.floor(seconds / 60);
|
|
41
|
-
const remainingSeconds = seconds % 60;
|
|
42
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Format date for display
|
|
46
|
-
*/
|
|
47
|
-
function formatDate(dateStr) {
|
|
48
|
-
if (!dateStr)
|
|
49
|
-
return 'N/A';
|
|
50
|
-
return new Date(dateStr).toLocaleDateString('en-US', {
|
|
51
|
-
year: 'numeric',
|
|
52
|
-
month: 'short',
|
|
53
|
-
day: 'numeric',
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Generate a markdown report for a feature review
|
|
58
|
-
*/
|
|
59
|
-
export function generateFeatureReport(data) {
|
|
60
|
-
const { feature, stats, checklistItems, sessions } = data;
|
|
61
|
-
const lines = [];
|
|
62
|
-
// Header
|
|
63
|
-
lines.push(`# Feature Review Report: ${feature.name}`);
|
|
64
|
-
lines.push('');
|
|
65
|
-
// Status - derive from completedAt and currentSession
|
|
66
|
-
let statusLabel;
|
|
67
|
-
let statusEmoji;
|
|
68
|
-
if (feature.completedAt) {
|
|
69
|
-
statusLabel = 'Completed';
|
|
70
|
-
statusEmoji = '\u2705'; // green check
|
|
71
|
-
}
|
|
72
|
-
else if (feature.currentSession) {
|
|
73
|
-
const sessionStatus = feature.currentSession.status;
|
|
74
|
-
switch (sessionStatus) {
|
|
75
|
-
case 'ready':
|
|
76
|
-
statusLabel = `Session ${feature.currentSession.iteration}: Ready`;
|
|
77
|
-
statusEmoji = '\u25b6\ufe0f'; // ▶️
|
|
78
|
-
break;
|
|
79
|
-
case 'in_progress':
|
|
80
|
-
statusLabel = `Session ${feature.currentSession.iteration}: In Progress`;
|
|
81
|
-
statusEmoji = '\ud83d\udd04'; // 🔄
|
|
82
|
-
break;
|
|
83
|
-
case 'completed':
|
|
84
|
-
statusLabel = `Session ${feature.currentSession.iteration}: Awaiting Review`;
|
|
85
|
-
statusEmoji = '\ud83d\udcca'; // 📊
|
|
86
|
-
break;
|
|
87
|
-
default:
|
|
88
|
-
statusLabel = 'Unknown';
|
|
89
|
-
statusEmoji = '\u2753';
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
statusLabel = 'No Session';
|
|
94
|
-
statusEmoji = '\u2b1c'; // white square
|
|
95
|
-
}
|
|
96
|
-
lines.push(`**Status:** ${statusEmoji} ${statusLabel}`);
|
|
97
|
-
lines.push(`**Created:** ${formatDate(feature.createdAt)}`);
|
|
98
|
-
if (feature.completedAt) {
|
|
99
|
-
lines.push(`**Completed:** ${formatDate(feature.completedAt)}`);
|
|
100
|
-
}
|
|
101
|
-
if (feature.gitRepoUrl) {
|
|
102
|
-
lines.push(`**Repository:** ${feature.gitRepoUrl}`);
|
|
103
|
-
}
|
|
104
|
-
if (feature.gitBranch) {
|
|
105
|
-
lines.push(`**Branch:** ${feature.gitBranch}`);
|
|
106
|
-
}
|
|
107
|
-
lines.push('');
|
|
108
|
-
// Description
|
|
109
|
-
if (feature.description) {
|
|
110
|
-
lines.push('## Description');
|
|
111
|
-
lines.push('');
|
|
112
|
-
lines.push(feature.description);
|
|
113
|
-
lines.push('');
|
|
114
|
-
}
|
|
115
|
-
// Scenario Progress
|
|
116
|
-
const nonClosedItems = checklistItems.filter((i) => i.status !== 'closed');
|
|
117
|
-
const verifiedCount = checklistItems.filter((i) => i.status === 'verified').length;
|
|
118
|
-
lines.push(`## Scenario Progress (${verifiedCount}/${nonClosedItems.length} verified)`);
|
|
119
|
-
lines.push('');
|
|
120
|
-
// Sort items by position
|
|
121
|
-
const sortedItems = [...checklistItems].sort((a, b) => a.position - b.position);
|
|
122
|
-
for (const item of sortedItems) {
|
|
123
|
-
const emoji = getStatusEmoji(item.status);
|
|
124
|
-
const isClosed = item.status === 'closed';
|
|
125
|
-
const prefix = isClosed ? '~~' : '';
|
|
126
|
-
const suffix = isClosed ? '~~' : '';
|
|
127
|
-
lines.push(`### ${emoji} ${prefix}${item.description}${suffix}`);
|
|
128
|
-
lines.push('');
|
|
129
|
-
if (item.status === 'verified' && item.verifiedAt) {
|
|
130
|
-
lines.push(`- **Verified:** ${formatDate(item.verifiedAt)}`);
|
|
131
|
-
}
|
|
132
|
-
if (item.status === 'blocked' && item.blockedAt) {
|
|
133
|
-
lines.push(`- **Blocked:** ${formatDate(item.blockedAt)}`);
|
|
134
|
-
if (item.blockedReason) {
|
|
135
|
-
lines.push(`- **Reason:** ${item.blockedReason}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (item.status === 'closed' && item.closedAt) {
|
|
139
|
-
lines.push(`- **Closed:** ${formatDate(item.closedAt)}`);
|
|
140
|
-
if (item.canceledReason) {
|
|
141
|
-
lines.push(`- **Reason:** ${item.canceledReason}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (item.status === 'incomplete') {
|
|
145
|
-
lines.push(`- **Status:** Incomplete - requires further work`);
|
|
146
|
-
}
|
|
147
|
-
// Link to session if available
|
|
148
|
-
if (item.browserSessionId) {
|
|
149
|
-
lines.push(`- **Session:** ${item.browserSessionId}`);
|
|
150
|
-
// Find the session and add trace link
|
|
151
|
-
const session = sessions.find((s) => s.id === item.browserSessionId);
|
|
152
|
-
if (session) {
|
|
153
|
-
if (session.agentResponse) {
|
|
154
|
-
lines.push(`- **Summary:** ${session.agentResponse}`);
|
|
155
|
-
}
|
|
156
|
-
if (session.traceUrl) {
|
|
157
|
-
lines.push(`- **Trace:** [View in Playwright](${buildTraceViewerUrl(session.traceUrl)})`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
lines.push('');
|
|
162
|
-
}
|
|
163
|
-
// Blocking Issues section
|
|
164
|
-
const blockedItems = checklistItems.filter((i) => i.status === 'blocked');
|
|
165
|
-
if (blockedItems.length > 0) {
|
|
166
|
-
lines.push('## Blocking Issues');
|
|
167
|
-
lines.push('');
|
|
168
|
-
for (const item of blockedItems) {
|
|
169
|
-
lines.push(`- **${item.description}**: ${item.blockedReason || 'No reason provided'}`);
|
|
170
|
-
}
|
|
171
|
-
lines.push('');
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
lines.push('## Blocking Issues');
|
|
175
|
-
lines.push('');
|
|
176
|
-
lines.push('None');
|
|
177
|
-
lines.push('');
|
|
178
|
-
}
|
|
179
|
-
// All Verification Sessions table
|
|
180
|
-
if (sessions.length > 0) {
|
|
181
|
-
lines.push('## All Verification Sessions');
|
|
182
|
-
lines.push('');
|
|
183
|
-
lines.push('| Session | Scenario | Status | Duration | Date |');
|
|
184
|
-
lines.push('|---------|----------|--------|----------|------|');
|
|
185
|
-
for (const session of sessions) {
|
|
186
|
-
const linkedItem = session.checklistItemId
|
|
187
|
-
? checklistItems.find((i) => i.id === session.checklistItemId)
|
|
188
|
-
: null;
|
|
189
|
-
const itemDesc = linkedItem
|
|
190
|
-
? linkedItem.description.substring(0, 30) +
|
|
191
|
-
(linkedItem.description.length > 30 ? '...' : '')
|
|
192
|
-
: 'N/A';
|
|
193
|
-
const statusEmoji = session.status === 'completed'
|
|
194
|
-
? '\u2705'
|
|
195
|
-
: session.status === 'failed'
|
|
196
|
-
? '\u274c'
|
|
197
|
-
: '\ud83d\udd04';
|
|
198
|
-
lines.push(`| ${session.id} | ${itemDesc} | ${statusEmoji} ${session.status} | ${formatDuration(session.durationMs)} | ${formatDate(session.createdAt)} |`);
|
|
199
|
-
}
|
|
200
|
-
lines.push('');
|
|
201
|
-
}
|
|
202
|
-
return lines.join('\n');
|
|
203
|
-
}
|
|
204
|
-
//# sourceMappingURL=featureReportGenerator.js.map
|
|
1
|
+
(function(_0x33637a,_0x327167){const _0x3ba240=_0x582b,_0x219b35=_0x33637a();while(!![]){try{const _0x5a8e23=-parseInt(_0x3ba240(0x201))/0x1*(parseInt(_0x3ba240(0x1f1))/0x2)+-parseInt(_0x3ba240(0x1f7))/0x3+-parseInt(_0x3ba240(0x20a))/0x4+parseInt(_0x3ba240(0x1e9))/0x5+-parseInt(_0x3ba240(0x1cc))/0x6*(parseInt(_0x3ba240(0x1fe))/0x7)+parseInt(_0x3ba240(0x1f6))/0x8*(parseInt(_0x3ba240(0x1f3))/0x9)+-parseInt(_0x3ba240(0x1c8))/0xa*(-parseInt(_0x3ba240(0x1df))/0xb);if(_0x5a8e23===_0x327167)break;else _0x219b35['push'](_0x219b35['shift']());}catch(_0x247f17){_0x219b35['push'](_0x219b35['shift']());}}}(_0x4c21,0x5e55e));function _0x4c21(){const _0x2bf882=['|---------|----------|--------|----------|------|','canceledReason','Completed','floor','**Completed:**\x20','1886616DBbjnc','-\x20**Closed:**\x20','Unknown','closedAt','createdAt','blockedReason','HRUuY','completed','pending',':\x20In\x20Progress','13120aSdNxO','N/A','position','durationMs','17436WiRIeV','currentSession','-\x20**Summary:**\x20','Zbjry','#\x20Feature\x20Review\x20Report:\x20','|\x20Session\x20|\x20Scenario\x20|\x20Status\x20|\x20Duration\x20|\x20Date\x20|','push','verifiedAt','en-US','length','gitRepoUrl','**:\x20','name','tMlaA','sort','incomplete','tyBJa','checklistItemId','faILz','7909GCPZlw','None','verified','browserSessionId','##\x20Scenario\x20Progress\x20(','Session\x20','find','in_progress','deVXy','ftMKa','3187725hPgUBO','blockedAt','short','...','HbGFF','substring','zoUBo','iteration','935590sPwXsw','filter','171LltIPe','description','closed','134840ZytQFM','792987ofToMX','##\x20Blocking\x20Issues','\x20|\x20','agentResponse','lxCBJ','uWuim','XJIDn','749NNbaZY','XsJxr','\x20verified)','1UHJyUB','completedAt',':\x20Ready','status'];_0x4c21=function(){return _0x2bf882;};return _0x4c21();}export function buildTraceViewerUrl(_0x1f6638){const _0x1fa01e={'zkACo':function(_0x2ba879,_0x1f71d2){return _0x2ba879(_0x1f71d2);}},_0x47758d=_0x1fa01e['zkACo'](encodeURIComponent,_0x1f6638);return'https://trace.playwright.dev/?trace='+_0x47758d;}function _0x582b(_0x3e1e66,_0x10bcf7){_0x3e1e66=_0x3e1e66-0x1c2;const _0x4c2108=_0x4c21();let _0x582bcf=_0x4c2108[_0x3e1e66];return _0x582bcf;}function getStatusEmoji(_0x247c8e){const _0x55aca4=_0x582b,_0x1a8b58={'aqwFd':'verified','uWuim':'completed','HVEzh':_0x55aca4(0x1e6)};switch(_0x247c8e){case _0x1a8b58['aqwFd']:case _0x1a8b58[_0x55aca4(0x1fc)]:return'✅';case _0x55aca4(0x1c6):case _0x1a8b58['HVEzh']:return'🔄';case _0x55aca4(0x1db):return'🟠';case'blocked':return'🛑';case _0x55aca4(0x1f5):return'⛔';default:return'❓';}}function formatDuration(_0x4127cc){const _0x2b0a9a=_0x582b,_0x3d137f={'XsJxr':function(_0x214342,_0xf230e0){return _0x214342===_0xf230e0;},'hBGoX':function(_0x457ce5,_0x371078){return _0x457ce5===_0x371078;},'XJIDn':'N/A','Vdzaa':function(_0x32ddf8,_0x4e47c1){return _0x32ddf8/_0x4e47c1;}};if(_0x3d137f[_0x2b0a9a(0x1ff)](_0x4127cc,null)||_0x3d137f['hBGoX'](_0x4127cc,undefined))return _0x3d137f[_0x2b0a9a(0x1fd)];if(_0x4127cc<0x3e8)return _0x4127cc+'ms';const _0x5d2c5f=Math['floor'](_0x4127cc/0x3e8);if(_0x5d2c5f<0x3c)return _0x5d2c5f+'s';const _0x3f6a5c=Math[_0x2b0a9a(0x208)](_0x3d137f['Vdzaa'](_0x5d2c5f,0x3c)),_0x52f28f=_0x5d2c5f%0x3c;return _0x3f6a5c+'m\x20'+_0x52f28f+'s';}function formatDate(_0x4676e0){const _0x58cfdf=_0x582b,_0x2e459a={'HbGFF':'numeric'};if(!_0x4676e0)return _0x58cfdf(0x1c9);return new Date(_0x4676e0)['toLocaleDateString'](_0x58cfdf(0x1d4),{'year':_0x2e459a[_0x58cfdf(0x1ed)],'month':_0x58cfdf(0x1eb),'day':_0x2e459a[_0x58cfdf(0x1ed)]});}export function generateFeatureReport(_0x40cff8){const _0x8f1b9a=_0x582b,_0x9f7617={'WoDtQ':_0x8f1b9a(0x207),'ToyNn':'in_progress','faILz':_0x8f1b9a(0x1c5),'ftMKa':_0x8f1b9a(0x20c),'lxCBJ':function(_0x81f873,_0x2e184d){return _0x81f873(_0x2e184d);},'lcjwS':_0x8f1b9a(0x1f5),'tyBJa':function(_0x584a17,_0x57d621){return _0x584a17===_0x57d621;},'LfiZa':'blocked','sFmXT':_0x8f1b9a(0x1db),'HRUuY':function(_0x21796c,_0x237f31){return _0x21796c>_0x237f31;},'deVXy':_0x8f1b9a(0x1e0),'zoUBo':'##\x20All\x20Verification\x20Sessions','tMlaA':_0x8f1b9a(0x1d1),'Zbjry':function(_0x124cf7,_0x4a0d31){return _0x124cf7(_0x4a0d31);}},{feature:_0xeaa72d,stats:_0x5c8051,checklistItems:_0x59b39c,sessions:_0x2ea3c7}=_0x40cff8,_0x334bcd=[];_0x334bcd[_0x8f1b9a(0x1d2)](_0x8f1b9a(0x1d0)+_0xeaa72d[_0x8f1b9a(0x1d8)]),_0x334bcd[_0x8f1b9a(0x1d2)]('');let _0x5e1461,_0x58c07c;if(_0xeaa72d[_0x8f1b9a(0x202)])_0x5e1461=_0x9f7617['WoDtQ'],_0x58c07c='✅';else{if(_0xeaa72d[_0x8f1b9a(0x1cd)]){const _0x570e42=_0xeaa72d[_0x8f1b9a(0x1cd)]['status'];switch(_0x570e42){case'ready':_0x5e1461=_0x8f1b9a(0x1e4)+_0xeaa72d[_0x8f1b9a(0x1cd)][_0x8f1b9a(0x1f0)]+_0x8f1b9a(0x203),_0x58c07c='▶️';break;case _0x9f7617['ToyNn']:_0x5e1461=_0x8f1b9a(0x1e4)+_0xeaa72d[_0x8f1b9a(0x1cd)]['iteration']+_0x8f1b9a(0x1c7),_0x58c07c='🔄';break;case _0x9f7617[_0x8f1b9a(0x1de)]:_0x5e1461=_0x8f1b9a(0x1e4)+_0xeaa72d[_0x8f1b9a(0x1cd)]['iteration']+':\x20Awaiting\x20Review',_0x58c07c='📊';break;default:_0x5e1461=_0x9f7617[_0x8f1b9a(0x1e8)],_0x58c07c='❓';}}else _0x5e1461='No\x20Session',_0x58c07c='⬜';}_0x334bcd['push']('**Status:**\x20'+_0x58c07c+'\x20'+_0x5e1461),_0x334bcd['push']('**Created:**\x20'+_0x9f7617[_0x8f1b9a(0x1fb)](formatDate,_0xeaa72d[_0x8f1b9a(0x1c2)]));_0xeaa72d[_0x8f1b9a(0x202)]&&_0x334bcd['push'](_0x8f1b9a(0x209)+formatDate(_0xeaa72d['completedAt']));_0xeaa72d['gitRepoUrl']&&_0x334bcd['push']('**Repository:**\x20'+_0xeaa72d[_0x8f1b9a(0x1d6)]);_0xeaa72d['gitBranch']&&_0x334bcd['push']('**Branch:**\x20'+_0xeaa72d['gitBranch']);_0x334bcd[_0x8f1b9a(0x1d2)]('');_0xeaa72d[_0x8f1b9a(0x1f4)]&&(_0x334bcd['push']('##\x20Description'),_0x334bcd['push'](''),_0x334bcd['push'](_0xeaa72d['description']),_0x334bcd[_0x8f1b9a(0x1d2)](''));const _0x18b7c9=_0x59b39c['filter'](_0x233ef7=>_0x233ef7['status']!==_0x8f1b9a(0x1f5)),_0x9e6ab0=_0x59b39c['filter'](_0x5d95ed=>_0x5d95ed[_0x8f1b9a(0x204)]==='verified')[_0x8f1b9a(0x1d5)];_0x334bcd[_0x8f1b9a(0x1d2)](_0x8f1b9a(0x1e3)+_0x9e6ab0+'/'+_0x18b7c9[_0x8f1b9a(0x1d5)]+_0x8f1b9a(0x200)),_0x334bcd['push']('');const _0x3a5a7d=[..._0x59b39c][_0x8f1b9a(0x1da)]((_0x5842e0,_0x24c1de)=>_0x5842e0[_0x8f1b9a(0x1ca)]-_0x24c1de['position']);for(const _0x209d49 of _0x3a5a7d){const _0x124066=_0x9f7617[_0x8f1b9a(0x1fb)](getStatusEmoji,_0x209d49[_0x8f1b9a(0x204)]),_0x11945b=_0x209d49[_0x8f1b9a(0x204)]===_0x9f7617['lcjwS'],_0x606d11=_0x11945b?'~~':'',_0xea23d6=_0x11945b?'~~':'';_0x334bcd[_0x8f1b9a(0x1d2)]('###\x20'+_0x124066+'\x20'+_0x606d11+_0x209d49[_0x8f1b9a(0x1f4)]+_0xea23d6),_0x334bcd[_0x8f1b9a(0x1d2)]('');_0x9f7617['tyBJa'](_0x209d49[_0x8f1b9a(0x204)],_0x8f1b9a(0x1e1))&&_0x209d49[_0x8f1b9a(0x1d3)]&&_0x334bcd['push']('-\x20**Verified:**\x20'+formatDate(_0x209d49['verifiedAt']));_0x209d49['status']===_0x9f7617['LfiZa']&&_0x209d49[_0x8f1b9a(0x1ea)]&&(_0x334bcd['push']('-\x20**Blocked:**\x20'+formatDate(_0x209d49[_0x8f1b9a(0x1ea)])),_0x209d49['blockedReason']&&_0x334bcd['push']('-\x20**Reason:**\x20'+_0x209d49[_0x8f1b9a(0x1c3)]));_0x209d49[_0x8f1b9a(0x204)]==='closed'&&_0x209d49[_0x8f1b9a(0x20d)]&&(_0x334bcd['push'](_0x8f1b9a(0x20b)+formatDate(_0x209d49[_0x8f1b9a(0x20d)])),_0x209d49[_0x8f1b9a(0x206)]&&_0x334bcd['push']('-\x20**Reason:**\x20'+_0x209d49[_0x8f1b9a(0x206)]));_0x9f7617[_0x8f1b9a(0x1dc)](_0x209d49['status'],_0x9f7617['sFmXT'])&&_0x334bcd[_0x8f1b9a(0x1d2)]('-\x20**Status:**\x20Incomplete\x20-\x20requires\x20further\x20work');if(_0x209d49[_0x8f1b9a(0x1e2)]){_0x334bcd[_0x8f1b9a(0x1d2)]('-\x20**Session:**\x20'+_0x209d49[_0x8f1b9a(0x1e2)]);const _0x1b6f43=_0x2ea3c7[_0x8f1b9a(0x1e5)](_0x3ae0d1=>_0x3ae0d1['id']===_0x209d49['browserSessionId']);_0x1b6f43&&(_0x1b6f43['agentResponse']&&_0x334bcd['push'](_0x8f1b9a(0x1ce)+_0x1b6f43[_0x8f1b9a(0x1fa)]),_0x1b6f43['traceUrl']&&_0x334bcd[_0x8f1b9a(0x1d2)]('-\x20**Trace:**\x20[View\x20in\x20Playwright]('+_0x9f7617['lxCBJ'](buildTraceViewerUrl,_0x1b6f43['traceUrl'])+')'));}_0x334bcd[_0x8f1b9a(0x1d2)]('');}const _0x5e14f3=_0x59b39c[_0x8f1b9a(0x1f2)](_0x43e8dd=>_0x43e8dd['status']==='blocked');if(_0x9f7617[_0x8f1b9a(0x1c4)](_0x5e14f3['length'],0x0)){_0x334bcd[_0x8f1b9a(0x1d2)]('##\x20Blocking\x20Issues'),_0x334bcd[_0x8f1b9a(0x1d2)]('');for(const _0x343f42 of _0x5e14f3){_0x334bcd[_0x8f1b9a(0x1d2)]('-\x20**'+_0x343f42['description']+_0x8f1b9a(0x1d7)+(_0x343f42['blockedReason']||'No\x20reason\x20provided'));}_0x334bcd['push']('');}else _0x334bcd[_0x8f1b9a(0x1d2)](_0x8f1b9a(0x1f8)),_0x334bcd['push'](''),_0x334bcd['push'](_0x9f7617[_0x8f1b9a(0x1e7)]),_0x334bcd[_0x8f1b9a(0x1d2)]('');if(_0x9f7617['HRUuY'](_0x2ea3c7['length'],0x0)){_0x334bcd['push'](_0x9f7617[_0x8f1b9a(0x1ef)]),_0x334bcd['push'](''),_0x334bcd[_0x8f1b9a(0x1d2)](_0x9f7617[_0x8f1b9a(0x1d9)]),_0x334bcd[_0x8f1b9a(0x1d2)](_0x8f1b9a(0x205));for(const _0x24a653 of _0x2ea3c7){const _0x54c3e2=_0x24a653[_0x8f1b9a(0x1dd)]?_0x59b39c['find'](_0x37caff=>_0x37caff['id']===_0x24a653['checklistItemId']):null,_0x10a63f=_0x54c3e2?_0x54c3e2['description'][_0x8f1b9a(0x1ee)](0x0,0x1e)+(_0x9f7617[_0x8f1b9a(0x1c4)](_0x54c3e2[_0x8f1b9a(0x1f4)]['length'],0x1e)?_0x8f1b9a(0x1ec):''):'N/A',_0xb8de51=_0x9f7617['tyBJa'](_0x24a653['status'],_0x8f1b9a(0x1c5))?'✅':_0x24a653['status']==='failed'?'❌':'🔄';_0x334bcd[_0x8f1b9a(0x1d2)]('|\x20'+_0x24a653['id']+_0x8f1b9a(0x1f9)+_0x10a63f+_0x8f1b9a(0x1f9)+_0xb8de51+'\x20'+_0x24a653[_0x8f1b9a(0x204)]+_0x8f1b9a(0x1f9)+formatDuration(_0x24a653[_0x8f1b9a(0x1cb)])+_0x8f1b9a(0x1f9)+_0x9f7617[_0x8f1b9a(0x1cf)](formatDate,_0x24a653['createdAt'])+'\x20|');}_0x334bcd[_0x8f1b9a(0x1d2)]('');}return _0x334bcd['join']('\x0a');}
|