trackqa 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -2
- package/dist/src/lib/mcpOnboarding.js +143 -0
- package/dist/tools/mcp-pilot/google-pkce.mjs +244 -0
- package/dist/tools/trackqa-connect/src/auth.js +153 -0
- package/dist/tools/trackqa-connect/src/cli.js +246 -0
- package/dist/tools/trackqa-connect/src/clients/adapter.js +3 -0
- package/dist/tools/trackqa-connect/src/clients/claude-session.js +78 -0
- package/dist/tools/trackqa-connect/src/clients/claude.js +113 -0
- package/dist/tools/trackqa-connect/src/clients/codex-verify.js +110 -0
- package/dist/tools/trackqa-connect/src/clients/codex.js +191 -0
- package/dist/tools/trackqa-connect/src/clients/gemini-config.js +141 -0
- package/dist/tools/trackqa-connect/src/clients/gemini-verify.js +14 -0
- package/dist/tools/trackqa-connect/src/clients/gemini-windows.js +134 -0
- package/dist/tools/trackqa-connect/src/clients/gemini.js +249 -0
- package/dist/tools/trackqa-connect/src/clients/vscode-copilot-lifecycle.js +97 -0
- package/dist/tools/trackqa-connect/src/clients/vscode-copilot-session.js +115 -0
- package/dist/tools/trackqa-connect/src/clients/vscode-copilot.js +59 -0
- package/dist/tools/trackqa-connect/src/clients/vscode-process.js +123 -0
- package/dist/tools/trackqa-connect/src/clients/vscode-session.js +145 -0
- package/dist/tools/trackqa-connect/src/clients/vscode.js +123 -0
- package/dist/tools/trackqa-connect/src/core.js +87 -0
- package/dist/tools/trackqa-connect/src/process.js +76 -0
- package/dist/tools/trackqa-connect/src/state.js +80 -0
- package/dist/tools/trackqa-connect/src/testing/copilot-fixture-scope.js +103 -0
- package/dist/tools/trackqa-connect/src/workflow.js +241 -0
- package/package.json +43 -4
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { ConnectorError, display, requireValue, setupFor, TOKEN, UUID } from "./core.js";
|
|
3
|
+
import { adapterClient, adapterHome, adapterLabel } from "./clients/adapter.js";
|
|
4
|
+
import { fingerprint, matchesEntry } from "./clients/codex.js";
|
|
5
|
+
import { recordHome } from "./state.js";
|
|
6
|
+
import { consumeFixturePermit, COPILOT_FIXTURE } from "./testing/copilot-fixture-scope.js";
|
|
7
|
+
export async function ownerProject(owner, id, archivedAllowed = false) {
|
|
8
|
+
requireValue(UUID.test(id), 'project_unavailable');
|
|
9
|
+
const membership = await owner.api(`/rest/v1/project_members?project_id=eq.${id}&user_id=eq.${owner.userId}&select=user_id,role&limit=2`);
|
|
10
|
+
const rows = await owner.api(`/rest/v1/projects?id=eq.${id}&select=id,name,key,mcp_enabled,archived&limit=2`);
|
|
11
|
+
requireValue(Array.isArray(membership) && membership.length === 1 && membership[0].user_id === owner.userId && membership[0].role === 'OWNER', 'owner_required');
|
|
12
|
+
requireValue(Array.isArray(rows) && rows.length === 1 && rows[0].id === id && typeof rows[0].mcp_enabled === 'boolean'
|
|
13
|
+
&& typeof rows[0].name === 'string' && typeof rows[0].key === 'string' && typeof rows[0].archived === 'boolean'
|
|
14
|
+
&& (archivedAllowed || !rows[0].archived), 'project_unavailable');
|
|
15
|
+
return rows[0];
|
|
16
|
+
}
|
|
17
|
+
export async function chooseProject(owner, ui, requested) {
|
|
18
|
+
const projects = owner.projects;
|
|
19
|
+
requireValue(projects.length, 'no_eligible_owner_projects');
|
|
20
|
+
const id = requested ?? projects[await ui.choose('Choose a project you own', projects.map(project => `${display(project.name)} [${display(project.key)}]`))]?.id;
|
|
21
|
+
requireValue(id && projects.some(project => project.id === id), 'project_unavailable');
|
|
22
|
+
return ownerProject(owner, id);
|
|
23
|
+
}
|
|
24
|
+
export function preview(setup, adapter) {
|
|
25
|
+
const command = adapter.installPreview?.(setup) ?? `codex mcp add ${setup.serverName} --url ${setup.endpoint} --bearer-token-env-var ${setup.envVar}`;
|
|
26
|
+
return `Client: ${adapterLabel(adapter)} CLI ${adapter.executable.version}\nProject: ${setup.projectId}\nConfig: ${display(adapterHome(adapter))}\nServer: ${setup.serverName}\nEndpoint: ${setup.endpoint}\nAuthentication: environment reference ${setup.envVar} (no token saved)\nInstallation method (preview only): ${command}\nUnrelated MCP entries will be preserved.`;
|
|
27
|
+
}
|
|
28
|
+
async function remoteCleanup(owner, record) {
|
|
29
|
+
await ownerProject(owner, record.projectId, true);
|
|
30
|
+
if (!['issuing', 'active', 'revocation_pending', 'revoked'].includes(record.phase))
|
|
31
|
+
return;
|
|
32
|
+
const filter = record.connectionId ? `id=eq.${record.connectionId}` : `client_label=eq.${encodeURIComponent(record.clientLabel)}&user_id=eq.${record.userId}`;
|
|
33
|
+
const route = `/rest/v1/mcp_connections?project_id=eq.${record.projectId}&${filter}&select=id,project_id,user_id,client_label,revoked_at&limit=2`;
|
|
34
|
+
let rows = await owner.api(route);
|
|
35
|
+
requireValue(Array.isArray(rows) && rows.length <= 1 && rows.every(row => UUID.test(row.id) && row.project_id === record.projectId && row.user_id === record.userId
|
|
36
|
+
&& (record.connectionId ? row.id === record.connectionId : row.client_label === record.clientLabel)), 'revocation_unconfirmed');
|
|
37
|
+
// A timed-out issuance may still commit later. Absence alone cannot settle it.
|
|
38
|
+
requireValue(record.connectionId || rows.length === 1, 'issuance_outcome_unknown');
|
|
39
|
+
if (!rows.length)
|
|
40
|
+
return; // Known ID absent; it cannot resolve to a live connection.
|
|
41
|
+
record.connectionId = rows[0].id;
|
|
42
|
+
if (!rows[0].revoked_at) {
|
|
43
|
+
await owner.api('/rest/v1/rpc/revoke_mcp_connection', 'POST', { p_connection_id: record.connectionId });
|
|
44
|
+
rows = await owner.api(route);
|
|
45
|
+
requireValue(Array.isArray(rows) && rows.length <= 1 && rows.every(row => row.id === record.connectionId && row.project_id === record.projectId && !!row.revoked_at), 'revocation_unconfirmed');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function cleanup(store, adapter, record, owner) {
|
|
49
|
+
requireValue(record.client === adapterClient(adapter) && recordHome(record) === adapterHome(adapter), 'configuration_home_mismatch');
|
|
50
|
+
const unresolved = [];
|
|
51
|
+
const needsRevocation = ['issuing', 'active', 'revocation_pending'].includes(record.phase);
|
|
52
|
+
if (needsRevocation) {
|
|
53
|
+
try {
|
|
54
|
+
requireValue(owner, 'owner_signin_required');
|
|
55
|
+
await remoteCleanup(owner, record);
|
|
56
|
+
record.phase = 'revoked';
|
|
57
|
+
await store.save(record);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
record.phase = 'revocation_pending';
|
|
61
|
+
unresolved.push('revocation_unconfirmed');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await adapter.remove(setupFor(record.projectId, record.client), record.configHash);
|
|
66
|
+
record.installed = false;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
unresolved.push('local_removal_unconfirmed');
|
|
70
|
+
}
|
|
71
|
+
if (unresolved.length)
|
|
72
|
+
await store.save(record);
|
|
73
|
+
else
|
|
74
|
+
await store.remove(record.projectId, record.client);
|
|
75
|
+
return { revoked: !needsRevocation || record.phase === 'revoked', removed: !record.installed && !unresolved.includes('local_removal_unconfirmed'), unresolved };
|
|
76
|
+
}
|
|
77
|
+
export async function connect(options) {
|
|
78
|
+
// Acceptance guard: stop even direct API callers before any Owner read,
|
|
79
|
+
// configuration write, project enablement or connection issuance.
|
|
80
|
+
requireValue(adapterClient(options.adapter) !== 'vscode-copilot', 'copilot_runtime_unverified');
|
|
81
|
+
return connectSession(options);
|
|
82
|
+
}
|
|
83
|
+
// No project/client flag or boolean guard bypass: this one-use identity-bound
|
|
84
|
+
// permit is created only by the separate, fixed-fixture acceptance orchestrator.
|
|
85
|
+
export async function connectCopilotFixture(options) {
|
|
86
|
+
requireValue(!('projectId' in options), 'fixture_identity_mismatch');
|
|
87
|
+
consumeFixturePermit(options.permit, options.owner, options.adapter, options.store);
|
|
88
|
+
return connectSession({ ...options, projectId: COPILOT_FIXTURE.id }, true);
|
|
89
|
+
}
|
|
90
|
+
async function connectSession({ owner, adapter, store, ui, projectId, signal }, fixture = false) {
|
|
91
|
+
const project = await chooseProject(owner, ui, projectId);
|
|
92
|
+
if (fixture)
|
|
93
|
+
requireValue(project.id === COPILOT_FIXTURE.id && project.name === COPILOT_FIXTURE.name && project.key === COPILOT_FIXTURE.key, 'fixture_identity_mismatch');
|
|
94
|
+
const client = adapterClient(adapter);
|
|
95
|
+
const setup = setupFor(project.id, client);
|
|
96
|
+
const records = await store.read();
|
|
97
|
+
requireValue(!records.some(record => record.client === client && (client === 'vscode-copilot' || record.projectId === project.id)), 'existing_connection_recover_first');
|
|
98
|
+
requireValue(await adapter.entry(setup) === null, 'configuration_conflict');
|
|
99
|
+
ui.say(preview(setup, adapter));
|
|
100
|
+
ui.say('The token will be available only to this foreground session. Other trusted processes on this OS account may inspect its environment. A normal future client launch will not inherit it.');
|
|
101
|
+
if (!await ui.confirm('Install this token-free configuration?'))
|
|
102
|
+
return;
|
|
103
|
+
requireValue(!signal?.aborted, 'interrupted');
|
|
104
|
+
const record = { projectId: project.id, userId: owner.userId, client, executable: adapter.executable.path,
|
|
105
|
+
...(client === 'vscode-copilot' ? { vscodeCopilotHome: adapterHome(adapter) } : client === 'vscode' ? { vscodeHome: adapterHome(adapter) } : client === 'claude' ? { claudeHome: adapterHome(adapter) } : client === 'gemini' ? { geminiHome: adapterHome(adapter) } : { codexHome: adapterHome(adapter) }), clientLabel: `TrackQA Connect ${randomUUID()}`, connectionId: null,
|
|
106
|
+
phase: 'planned', installed: false, configHash: null, lastVerifiedAt: null };
|
|
107
|
+
await store.save(record);
|
|
108
|
+
let token = '';
|
|
109
|
+
let failure;
|
|
110
|
+
const run = async () => {
|
|
111
|
+
record.configHash = await adapter.install(setup);
|
|
112
|
+
record.installed = true;
|
|
113
|
+
record.phase = 'installed';
|
|
114
|
+
await store.save(record);
|
|
115
|
+
let current = await ownerProject(owner, project.id);
|
|
116
|
+
if (!current.mcp_enabled) {
|
|
117
|
+
ui.say(fixture ? 'Acceptance only: MCP will be restored to its verified disabled baseline. No other project is authorized.' : 'Enabling project access can also restore other eligible connections. This setting remains enabled after your session; only this session’s token will be revoked automatically.');
|
|
118
|
+
if (!await ui.confirm(`Enable MCP access for ${display(current.name)} only?`))
|
|
119
|
+
return;
|
|
120
|
+
requireValue(!signal?.aborted, 'interrupted');
|
|
121
|
+
const result = await owner.api(`/rest/v1/projects?id=eq.${project.id}&select=id,mcp_enabled`, 'PATCH', { mcp_enabled: true });
|
|
122
|
+
requireValue(Array.isArray(result) && result.length === 1 && result[0].id === project.id && result[0].mcp_enabled === true, 'enablement_unconfirmed');
|
|
123
|
+
current = await ownerProject(owner, project.id);
|
|
124
|
+
}
|
|
125
|
+
requireValue(current.mcp_enabled && !signal?.aborted, 'project_unavailable');
|
|
126
|
+
if (!await ui.confirm(`Approve one connection for your account on ${display(current.name)}? Default expiry is 90 days; revocation will be attempted when this session ends.`))
|
|
127
|
+
return;
|
|
128
|
+
requireValue(!signal?.aborted, 'interrupted');
|
|
129
|
+
record.phase = 'issuing';
|
|
130
|
+
await store.save(record);
|
|
131
|
+
const rows = await owner.api('/rest/v1/rpc/issue_mcp_connection_token', 'POST', { p_project_id: project.id, p_user_id: owner.userId, p_client_label: record.clientLabel });
|
|
132
|
+
try {
|
|
133
|
+
requireValue(Array.isArray(rows) && rows.length === 1 && UUID.test(rows[0].connection_id), 'issuance_unconfirmed');
|
|
134
|
+
record.connectionId = rows[0].connection_id;
|
|
135
|
+
await store.save(record);
|
|
136
|
+
requireValue(TOKEN.test(rows[0].access_token), 'issuance_unconfirmed');
|
|
137
|
+
token = rows[0].access_token;
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
if (Array.isArray(rows))
|
|
141
|
+
rows.forEach(row => { if (row && typeof row === 'object')
|
|
142
|
+
row.access_token = ''; });
|
|
143
|
+
}
|
|
144
|
+
record.phase = 'active';
|
|
145
|
+
await store.save(record);
|
|
146
|
+
requireValue(!signal?.aborted, 'interrupted');
|
|
147
|
+
const verification = await adapter.verify(setup, token, signal);
|
|
148
|
+
ui.say(`Configured: yes. Authorized: ${verification.authorized === true ? 'verified' : 'not verified'}. Verified working: ${verification.verified ? 'yes' : 'no'}.`);
|
|
149
|
+
if (verification.code === 'manual_verification_required') {
|
|
150
|
+
ui.say(adapter.manualInstructions?.(setup) ?? `Automatic client verification is unavailable. In Codex, use /mcp and call trackqa_connection_status and trackqa_list_projects; confirm only project ${project.id}. Do not submit a finding. The CLI will keep the result unverified.`);
|
|
151
|
+
if (!await ui.confirm('Launch for manual read-only verification?'))
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
else
|
|
155
|
+
requireValue(verification.verified, verification.code);
|
|
156
|
+
if (verification.verified) {
|
|
157
|
+
record.lastVerifiedAt = new Date().toISOString();
|
|
158
|
+
await store.save(record);
|
|
159
|
+
}
|
|
160
|
+
requireValue(!signal?.aborted, 'interrupted');
|
|
161
|
+
ui.say(client === 'vscode-copilot'
|
|
162
|
+
? `Launching ${adapterLabel(adapter)}. Keep the CLI open; type disconnect here to revoke access. Editor exit is not proof that Copilot sessions terminated.`
|
|
163
|
+
: `Launching ${adapterLabel(adapter)}. Exit normally to return here for token revocation and configuration cleanup.`);
|
|
164
|
+
await adapter.launch(setup, token, signal);
|
|
165
|
+
};
|
|
166
|
+
try {
|
|
167
|
+
await run();
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
failure = error;
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
token = '';
|
|
174
|
+
let result;
|
|
175
|
+
if (fixture) {
|
|
176
|
+
try {
|
|
177
|
+
await disconnect(store, adapter, ui, async (action) => action(owner), COPILOT_FIXTURE.id);
|
|
178
|
+
result = { revoked: true, removed: true, unresolved: [] };
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
const pending = (await store.read()).find(row => row.client === 'vscode-copilot' && row.projectId === COPILOT_FIXTURE.id);
|
|
182
|
+
result = { revoked: pending?.phase === 'revoked', removed: pending?.installed === false, unresolved: ['acceptance_disconnect_unconfirmed'] };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else
|
|
186
|
+
result = await cleanup(store, adapter, record, owner);
|
|
187
|
+
ui.say(`Session cleanup: local configuration ${result.removed ? 'removed' : 'UNCONFIRMED'}; remote revocation ${result.revoked ? 'confirmed or not needed' : 'UNCONFIRMED'}.`);
|
|
188
|
+
if (result.unresolved.length) {
|
|
189
|
+
if (client === 'gemini')
|
|
190
|
+
ui.say('Preserve settings.json and any .trackqa-settings-recovery-*.bak in the Gemini home. Compare/merge before recovery; never restore over an unexpected external edit or retry an uncertain write automatically.');
|
|
191
|
+
if (client === 'vscode')
|
|
192
|
+
ui.say('Close the entire TrackQA Local instance. Keep its process.json and recovery record until termination is verified; do not remove a pending marker to bypass recovery. Generated IDE data and unexpected files are preserved.');
|
|
193
|
+
ui.say(`Recovery required: run disconnect --client ${client} --project ${project.id}, or ask a project Owner to revoke the connection in Manage Connections. Keep the nonsecret recovery record. No token was saved.`);
|
|
194
|
+
failure = new ConnectorError('cleanup_unconfirmed');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (failure)
|
|
198
|
+
throw failure;
|
|
199
|
+
}
|
|
200
|
+
export async function status(store, adapter, ui, projectId) {
|
|
201
|
+
const records = (await store.read()).filter(record => record.client === adapterClient(adapter) && (!projectId || record.projectId === projectId));
|
|
202
|
+
if (!records.length) {
|
|
203
|
+
ui.say('No connector-managed configuration or recovery records. Authorized: not checked. Verified working: not checked.');
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
for (const record of records) {
|
|
207
|
+
requireValue(recordHome(record) === adapterHome(adapter), 'configuration_home_mismatch');
|
|
208
|
+
const setup = setupFor(record.projectId, record.client);
|
|
209
|
+
const entry = await adapter.entry(setup);
|
|
210
|
+
const configured = !!record.configHash && (adapter.matches ?? matchesEntry)(entry, setup) && fingerprint(entry) === record.configHash;
|
|
211
|
+
ui.say(`Project ${record.projectId}: Configured: ${configured ? 'yes' : entry ? 'changed; not managed safely' : 'no'}. Authorized: not checked. Verified working: not checked (no live session probe). Recovery: ${record.phase}. Last verified: ${record.lastVerifiedAt ?? 'never'}.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export async function disconnect(store, adapter, ui, authenticate, projectId) {
|
|
215
|
+
const records = (await store.read()).filter(record => record.client === adapterClient(adapter) && (!projectId || record.projectId === projectId));
|
|
216
|
+
requireValue(records.length, 'no_managed_connections');
|
|
217
|
+
const record = records.length === 1 ? records[0] : records[await ui.choose('Choose a connection to remove', records.map(record => record.projectId))];
|
|
218
|
+
requireValue(record && recordHome(record) === adapterHome(adapter), 'configuration_home_mismatch');
|
|
219
|
+
if (!await ui.confirm(`Remove the connector-owned configuration and revoke its connection for project ${record.projectId}?`))
|
|
220
|
+
return;
|
|
221
|
+
let result, authenticationFailure;
|
|
222
|
+
if (!['issuing', 'active', 'revocation_pending'].includes(record.phase))
|
|
223
|
+
result = await cleanup(store, adapter, record);
|
|
224
|
+
else {
|
|
225
|
+
try {
|
|
226
|
+
await authenticate(async (owner) => { result = await cleanup(store, adapter, record, owner); });
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
authenticationFailure = error;
|
|
230
|
+
result = await cleanup(store, adapter, record);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
requireValue(result, 'cleanup_unconfirmed');
|
|
234
|
+
ui.say(`Disconnect: local configuration ${result.removed ? 'removed' : 'UNCONFIRMED'}; remote revocation ${result.revoked ? 'confirmed or not needed' : 'UNCONFIRMED'}.`);
|
|
235
|
+
if (result.unresolved.length && adapterClient(adapter) === 'gemini')
|
|
236
|
+
ui.say('Preserve settings.json, any .trackqa-settings-recovery-*.bak, and the nonsecret recovery record. Compare/merge before retrying local recovery; never reset the whole settings file.');
|
|
237
|
+
if (result.unresolved.length)
|
|
238
|
+
throw new ConnectorError('cleanup_unconfirmed');
|
|
239
|
+
if (authenticationFailure)
|
|
240
|
+
throw authenticationFailure;
|
|
241
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trackqa",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
}
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Owner-operated TrackQA MCP connector for Windows",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": { "trackqa": "dist/tools/trackqa-connect/src/cli.js" },
|
|
7
|
+
"files": [
|
|
8
|
+
"README.md",
|
|
9
|
+
"dist/src/lib/mcpOnboarding.js",
|
|
10
|
+
"dist/tools/mcp-pilot/google-pkce.mjs",
|
|
11
|
+
"dist/tools/trackqa-connect/src/auth.js",
|
|
12
|
+
"dist/tools/trackqa-connect/src/cli.js",
|
|
13
|
+
"dist/tools/trackqa-connect/src/core.js",
|
|
14
|
+
"dist/tools/trackqa-connect/src/process.js",
|
|
15
|
+
"dist/tools/trackqa-connect/src/state.js",
|
|
16
|
+
"dist/tools/trackqa-connect/src/workflow.js",
|
|
17
|
+
"dist/tools/trackqa-connect/src/clients/adapter.js",
|
|
18
|
+
"dist/tools/trackqa-connect/src/clients/claude-session.js",
|
|
19
|
+
"dist/tools/trackqa-connect/src/clients/claude.js",
|
|
20
|
+
"dist/tools/trackqa-connect/src/clients/codex-verify.js",
|
|
21
|
+
"dist/tools/trackqa-connect/src/clients/codex.js",
|
|
22
|
+
"dist/tools/trackqa-connect/src/clients/gemini-config.js",
|
|
23
|
+
"dist/tools/trackqa-connect/src/clients/gemini-verify.js",
|
|
24
|
+
"dist/tools/trackqa-connect/src/clients/gemini-windows.js",
|
|
25
|
+
"dist/tools/trackqa-connect/src/clients/gemini.js",
|
|
26
|
+
"dist/tools/trackqa-connect/src/clients/vscode-copilot-lifecycle.js",
|
|
27
|
+
"dist/tools/trackqa-connect/src/clients/vscode-copilot-session.js",
|
|
28
|
+
"dist/tools/trackqa-connect/src/clients/vscode-copilot.js",
|
|
29
|
+
"dist/tools/trackqa-connect/src/clients/vscode-process.js",
|
|
30
|
+
"dist/tools/trackqa-connect/src/clients/vscode-session.js",
|
|
31
|
+
"dist/tools/trackqa-connect/src/clients/vscode.js",
|
|
32
|
+
"dist/tools/trackqa-connect/src/testing/copilot-fixture-scope.js"
|
|
33
|
+
],
|
|
34
|
+
"engines": { "node": ">=22.19.0" },
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "node --experimental-strip-types src/cli.ts",
|
|
37
|
+
"test": "node --experimental-strip-types --experimental-test-isolation=none --test tests/*.test.mjs",
|
|
38
|
+
"check": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
|
|
39
|
+
"build": "node scripts/build.mjs",
|
|
40
|
+
"prepack": "node scripts/build.mjs",
|
|
41
|
+
"start": "node dist/tools/trackqa-connect/src/cli.js",
|
|
42
|
+
"lint": "node ../../node_modules/oxlint/bin/oxlint src tests"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": { "@supabase/supabase-js": "2.113.0" }
|
|
45
|
+
}
|