@popoverai/dotrequirements 0.32.0 → 0.33.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/dist/cli.js +19 -6
- package/dist/cloud.d.ts +14 -0
- package/dist/cloud.js +34 -0
- package/dist/commands/create-requirement-document.js +2 -2
- package/dist/commands/init.js +7 -6
- package/dist/commands/link.js +27 -21
- package/dist/commands/report.js +22 -9
- package/dist/commands/review-test.js +3 -2
- package/dist/commands/style-check.js +4 -4
- package/dist/convex.d.ts +10 -12
- package/dist/convex.js +11 -12
- package/dist/harness/finalize.js +16 -8
- package/dist/harness/reportingStatus.js +2 -11
- package/dist/requirements/cloud-ai.d.ts +0 -6
- package/dist/requirements/cloud-ai.js +2 -11
- package/dist/requirements/cloud-coverage.js +4 -4
- package/dist/sync/execute.js +7 -19
- package/dist/sync/publish.js +2 -2
- package/dist/sync/snapshot.d.ts +4 -4
- package/dist/sync/snapshot.js +17 -12
- package/dist/sync/types.d.ts +1 -1
- package/dist/templates/example-requirements.js +2 -2
- package/dist/templates/example-requirements.ts +2 -2
- package/dist/utils/command-error.d.ts +19 -0
- package/dist/utils/command-error.js +44 -0
- package/dist/utils/oauth-flow.js +2 -2
- package/dist/utils/project-selector.js +4 -4
- package/dist/version-messages.d.ts +28 -0
- package/dist/version-messages.js +87 -0
- package/package.json +1 -1
- package/dist/config.d.ts +0 -5
- package/dist/config.js +0 -12
package/dist/cli.js
CHANGED
|
@@ -25,8 +25,10 @@ import { styleCheckCommand } from "./commands/style-check.js";
|
|
|
25
25
|
import { syncCliAction } from "./commands/sync.js";
|
|
26
26
|
import { testsForCommand } from "./commands/tests-for.js";
|
|
27
27
|
import { validateCommand } from "./commands/validate.js";
|
|
28
|
+
import { commandErrorMessage } from "./utils/command-error.js";
|
|
28
29
|
import { loadEnvFile } from "./utils/env.js";
|
|
29
30
|
import { setPreferEnvCredentials } from "./utils/project-settings.js";
|
|
31
|
+
import { enableVersionMessages, formatVersionMessages, markVersionMessagesShown, versionMessages, } from "./version-messages.js";
|
|
30
32
|
// Read version from package.json
|
|
31
33
|
const __filename = fileURLToPath(import.meta.url);
|
|
32
34
|
const __dirname = dirname(__filename);
|
|
@@ -34,6 +36,20 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"),
|
|
|
34
36
|
const VERSION = packageJson.version;
|
|
35
37
|
// Load environment variables from .env.local if it exists
|
|
36
38
|
loadEnvFile();
|
|
39
|
+
// CLI-MSG-1: a run that reaches the cloud hears what it has to say to this
|
|
40
|
+
// version.
|
|
41
|
+
enableVersionMessages();
|
|
42
|
+
/**
|
|
43
|
+
* The cloud's messages for this version, after the command's own output. On
|
|
44
|
+
* stderr, so a command's piped output stays clean.
|
|
45
|
+
*/
|
|
46
|
+
async function printVersionMessages() {
|
|
47
|
+
const messages = await versionMessages();
|
|
48
|
+
if (messages.length > 0) {
|
|
49
|
+
console.error(`${formatVersionMessages(messages)}\n`);
|
|
50
|
+
}
|
|
51
|
+
markVersionMessagesShown();
|
|
52
|
+
}
|
|
37
53
|
/**
|
|
38
54
|
* Wrap command handlers to catch errors and display them cleanly
|
|
39
55
|
* without showing stack traces to users
|
|
@@ -44,14 +60,11 @@ function wrapCommand(fn) {
|
|
|
44
60
|
await fn(...args);
|
|
45
61
|
}
|
|
46
62
|
catch (error) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
console.error(`\nError: ${String(error)}\n`);
|
|
52
|
-
}
|
|
63
|
+
console.error(`\nError: ${commandErrorMessage(error)}\n`);
|
|
64
|
+
await printVersionMessages();
|
|
53
65
|
process.exit(1);
|
|
54
66
|
}
|
|
67
|
+
await printVersionMessages();
|
|
55
68
|
};
|
|
56
69
|
}
|
|
57
70
|
const program = new Command();
|
package/dist/cloud.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one way into the dotrequirements cloud. Every place the CLI reaches the
|
|
3
|
+
* cloud asks here for the address, so the first use in a run is visible — the
|
|
4
|
+
* version-message check (CLI-MSG-1) starts there.
|
|
5
|
+
*/
|
|
6
|
+
/** The web app, home of the hosted AI endpoints and publishing. */
|
|
7
|
+
export declare const DEFAULT_API_BASE_URL = "https://app.dotrequirements.io";
|
|
8
|
+
/** The Convex backend's address, for a ConvexHttpClient or its HTTP API. */
|
|
9
|
+
export declare function convexUrl(): string;
|
|
10
|
+
/** The web app's address; `DOTREQUIREMENTS_API_URL` overrides it. */
|
|
11
|
+
export declare function webApiUrl(): string;
|
|
12
|
+
/** Run `callback` the first time this process reaches the cloud. */
|
|
13
|
+
export declare function whenCloudFirstUsed(callback: () => void): void;
|
|
14
|
+
//# sourceMappingURL=cloud.d.ts.map
|
package/dist/cloud.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one way into the dotrequirements cloud. Every place the CLI reaches the
|
|
3
|
+
* cloud asks here for the address, so the first use in a run is visible — the
|
|
4
|
+
* version-message check (CLI-MSG-1) starts there.
|
|
5
|
+
*/
|
|
6
|
+
/** The Convex backend every user's CLI talks to. */
|
|
7
|
+
const CONVEX_URL = "https://data.dotrequirements.io";
|
|
8
|
+
/** The web app, home of the hosted AI endpoints and publishing. */
|
|
9
|
+
export const DEFAULT_API_BASE_URL = "https://app.dotrequirements.io";
|
|
10
|
+
let onFirstUse = null;
|
|
11
|
+
let used = false;
|
|
12
|
+
function noteUse() {
|
|
13
|
+
if (used)
|
|
14
|
+
return;
|
|
15
|
+
used = true;
|
|
16
|
+
onFirstUse?.();
|
|
17
|
+
}
|
|
18
|
+
/** The Convex backend's address, for a ConvexHttpClient or its HTTP API. */
|
|
19
|
+
export function convexUrl() {
|
|
20
|
+
noteUse();
|
|
21
|
+
return CONVEX_URL;
|
|
22
|
+
}
|
|
23
|
+
/** The web app's address; `DOTREQUIREMENTS_API_URL` overrides it. */
|
|
24
|
+
export function webApiUrl() {
|
|
25
|
+
noteUse();
|
|
26
|
+
return process.env.DOTREQUIREMENTS_API_URL ?? DEFAULT_API_BASE_URL;
|
|
27
|
+
}
|
|
28
|
+
/** Run `callback` the first time this process reaches the cloud. */
|
|
29
|
+
export function whenCloudFirstUsed(callback) {
|
|
30
|
+
onFirstUse = callback;
|
|
31
|
+
if (used)
|
|
32
|
+
callback();
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=cloud.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { convexUrl } from "../cloud.js";
|
|
1
2
|
import { getProjectContext } from "../requirements/cloud-ai.js";
|
|
2
3
|
import { loadAllRequirements } from "../requirements/index.js";
|
|
3
4
|
import { generateStyleGuide } from "../requirements/style-guide.js";
|
|
4
5
|
import { readLocalStyleGuide } from "../requirements/style-guide-file.js";
|
|
5
6
|
import { findProjectRoot, getProjectCredentials, } from "../utils/project-settings.js";
|
|
6
|
-
const CONVEX_URL = "https://data.dotrequirements.io";
|
|
7
7
|
export async function createRequirementDocumentCommand(filePath) {
|
|
8
8
|
const workspaceRoot = process.cwd();
|
|
9
9
|
// CLI-CREATE-DOC-1.1 / .2: discover existing patterns from the workspace.
|
|
@@ -24,7 +24,7 @@ export async function createRequirementDocumentCommand(filePath) {
|
|
|
24
24
|
if (projectRoot) {
|
|
25
25
|
try {
|
|
26
26
|
const { projectId, projectSecret } = getProjectCredentials(workspaceRoot);
|
|
27
|
-
const contextData = await getProjectContext(projectId, projectSecret,
|
|
27
|
+
const contextData = await getProjectContext(projectId, projectSecret, convexUrl());
|
|
28
28
|
customStyleGuidance = contextData?.requirementsStyleContext ?? null;
|
|
29
29
|
}
|
|
30
30
|
catch {
|
package/dist/commands/init.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { ConvexHttpClient } from "convex/browser";
|
|
4
|
-
import {
|
|
4
|
+
import { convexUrl } from "../cloud.js";
|
|
5
5
|
import { api } from "../convex.js";
|
|
6
6
|
import { generateStyleGuideBody } from "../requirements/style-guide.js";
|
|
7
7
|
import { generateExampleRequirements } from "../templates/example-requirements.js";
|
|
8
8
|
import { generateRequirementsReadme } from "../templates/requirements-readme.js";
|
|
9
9
|
import { brand } from "../utils/brand.js";
|
|
10
|
+
import { commandErrorMessage } from "../utils/command-error.js";
|
|
10
11
|
import { ensureGitignore } from "../utils/gitignore.js";
|
|
11
12
|
import { executeOAuthFlow } from "../utils/oauth-flow.js";
|
|
12
13
|
import { detectProjectName } from "../utils/project-name.js";
|
|
@@ -69,7 +70,7 @@ export async function initCommand(options = {}) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
catch (error) {
|
|
72
|
-
console.error(`\n✗ Error: ${
|
|
73
|
+
console.error(`\n✗ Error: ${commandErrorMessage(error)}`);
|
|
73
74
|
// INIT-11: Network error guidance
|
|
74
75
|
if (error instanceof Error && error.message.includes("fetch")) {
|
|
75
76
|
console.error("\nCheck your internet connection and try again.");
|
|
@@ -130,7 +131,7 @@ async function authenticatedFlow(cwd, options) {
|
|
|
130
131
|
return false;
|
|
131
132
|
}
|
|
132
133
|
// Set up authenticated client using the session token
|
|
133
|
-
const client = new ConvexHttpClient(
|
|
134
|
+
const client = new ConvexHttpClient(convexUrl());
|
|
134
135
|
client.setAuth(oauthResult.accessToken);
|
|
135
136
|
// INIT-18: Select team
|
|
136
137
|
const selectedTeam = await selectTeam(client);
|
|
@@ -216,9 +217,9 @@ async function selectTeam(client) {
|
|
|
216
217
|
*/
|
|
217
218
|
async function chooseProjectAction(client, teamId) {
|
|
218
219
|
// INIT-7.3: Check if team has existing projects the user can access
|
|
219
|
-
const projectsWithSecrets = (await client.query(api.projectSecrets.queries.
|
|
220
|
+
const projectsWithSecrets = (await client.query(api.projectSecrets.queries.listOwnForTeam,
|
|
220
221
|
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id<"teams"> type; the runtime value is a plain string the convex server validates.
|
|
221
|
-
{
|
|
222
|
+
{ teamId: teamId }));
|
|
222
223
|
if (projectsWithSecrets.length === 0) {
|
|
223
224
|
// No existing projects, only offer create
|
|
224
225
|
const confirmed = await promptConfirm("This team has no projects. Create a new one?", true);
|
|
@@ -315,7 +316,7 @@ async function connectExistingProjectFlow(cwd, client, team) {
|
|
|
315
316
|
*/
|
|
316
317
|
async function inviteFlow(cwd, token) {
|
|
317
318
|
// MEMBERSHIP-18.0: Validate token before auth (no auth needed for this query)
|
|
318
|
-
const client = new ConvexHttpClient(
|
|
319
|
+
const client = new ConvexHttpClient(convexUrl());
|
|
319
320
|
console.log("Validating invite token...");
|
|
320
321
|
const inviteInfo = await client.query(api.teamInvites.queries.getInviteInfo, {
|
|
321
322
|
token,
|
package/dist/commands/link.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { ConvexHttpClient } from "convex/browser";
|
|
4
|
-
import {
|
|
4
|
+
import { convexUrl } from "../cloud.js";
|
|
5
5
|
import { api } from "../convex.js";
|
|
6
6
|
import { WEB_APP_URL } from "../push/index.js";
|
|
7
7
|
import { brand } from "../utils/brand.js";
|
|
8
|
+
import { commandErrorMessage } from "../utils/command-error.js";
|
|
8
9
|
import { ensureGitignore } from "../utils/gitignore.js";
|
|
9
10
|
import { executeOAuthFlow } from "../utils/oauth-flow.js";
|
|
10
11
|
import { getOrCreateProjectSecret, promptExpiryDays, selectTeam, } from "../utils/project-selector.js";
|
|
11
12
|
import { findProjectRoot, readProjectSettings, writeProjectSettings, } from "../utils/project-settings.js";
|
|
12
13
|
import { promptChoice, promptConfirm } from "../utils/prompts.js";
|
|
14
|
+
import { markVersionMessagesShown, versionMessages, } from "../version-messages.js";
|
|
13
15
|
import { hasCapacity, isNonInteractive, resolveProject, resolveProjectName, resolveTeam, } from "./link-resolution.js";
|
|
14
16
|
import { syncCommand } from "./sync.js";
|
|
15
17
|
/**
|
|
@@ -47,10 +49,13 @@ async function runNonInteractiveLink(options) {
|
|
|
47
49
|
if (options.json) {
|
|
48
50
|
console.log = (...args) => console.error(...args);
|
|
49
51
|
}
|
|
52
|
+
// CLI-MSG-1.1: the JSON result carries the cloud's messages, so it is
|
|
53
|
+
// printed once the command is done and they are in.
|
|
54
|
+
let jsonOutcome = null;
|
|
50
55
|
const finish = (outcome) => {
|
|
51
56
|
console.log = originalLog;
|
|
52
57
|
if (options.json) {
|
|
53
|
-
|
|
58
|
+
jsonOutcome = outcome;
|
|
54
59
|
}
|
|
55
60
|
else if (outcome.status === "decision_needed") {
|
|
56
61
|
console.log(`\n${outcome.message}`);
|
|
@@ -101,7 +106,7 @@ async function runNonInteractiveLink(options) {
|
|
|
101
106
|
}
|
|
102
107
|
// LINK-3 / LINK-10.7: browser auth is the only human step
|
|
103
108
|
const oauthResult = await executeOAuthFlow();
|
|
104
|
-
const client = new ConvexHttpClient(
|
|
109
|
+
const client = new ConvexHttpClient(convexUrl());
|
|
105
110
|
client.setAuth(oauthResult.accessToken);
|
|
106
111
|
await client.mutation(api.teams.mutations.ensureDefaultTeam, {});
|
|
107
112
|
const teams = (await client.query(api.teams.queries.listForUserWithUsage));
|
|
@@ -201,12 +206,9 @@ async function runNonInteractiveLink(options) {
|
|
|
201
206
|
if (projectResolution.rename) {
|
|
202
207
|
const newName = resolveProjectName(options, readPackageName(cwd), path.basename(cwd));
|
|
203
208
|
try {
|
|
204
|
-
await client.mutation(api.projects.mutations.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id type; the runtime value is a plain string the server validates.
|
|
208
|
-
id: projectResolution.project.projectId,
|
|
209
|
-
},
|
|
209
|
+
await client.mutation(api.projects.mutations.updateProject, {
|
|
210
|
+
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id type; the runtime value is a plain string the server validates.
|
|
211
|
+
projectId: projectResolution.project.projectId,
|
|
210
212
|
name: newName,
|
|
211
213
|
});
|
|
212
214
|
projectName = newName;
|
|
@@ -234,13 +236,17 @@ async function runNonInteractiveLink(options) {
|
|
|
234
236
|
});
|
|
235
237
|
}
|
|
236
238
|
catch (error) {
|
|
237
|
-
finish({
|
|
238
|
-
status: "error",
|
|
239
|
-
message: error instanceof Error ? error.message : String(error),
|
|
240
|
-
});
|
|
239
|
+
finish({ status: "error", message: commandErrorMessage(error) });
|
|
241
240
|
}
|
|
242
241
|
finally {
|
|
243
242
|
console.log = originalLog;
|
|
243
|
+
// Here, not after the block: every outcome above returns from inside
|
|
244
|
+
// `try`. Assigned inside `finish`, which TypeScript can't follow.
|
|
245
|
+
const outcome = jsonOutcome;
|
|
246
|
+
if (outcome) {
|
|
247
|
+
originalLog(JSON.stringify({ ...outcome, messages: await versionMessages() }));
|
|
248
|
+
markVersionMessagesShown();
|
|
249
|
+
}
|
|
244
250
|
}
|
|
245
251
|
}
|
|
246
252
|
/**
|
|
@@ -253,18 +259,18 @@ async function runNonInteractiveLink(options) {
|
|
|
253
259
|
async function mintShareArtifacts(client, teamId, projectId) {
|
|
254
260
|
const artifacts = {};
|
|
255
261
|
try {
|
|
256
|
-
const invite = (await client.mutation(api.teamInvites.mutations.
|
|
262
|
+
const invite = (await client.mutation(api.teamInvites.mutations.createInviteLink,
|
|
257
263
|
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id type; the runtime value is a plain string the server validates.
|
|
258
|
-
{
|
|
264
|
+
{ teamId: teamId }));
|
|
259
265
|
artifacts.inviteUrl = `${WEB_APP_URL}/invite/${invite.token}`;
|
|
260
266
|
}
|
|
261
267
|
catch {
|
|
262
268
|
// LINK-13.3: admin-gated — omit the invite URL for non-admin members
|
|
263
269
|
}
|
|
264
270
|
try {
|
|
265
|
-
const share = (await client.mutation(api.projectSecrets.mutations.
|
|
271
|
+
const share = (await client.mutation(api.projectSecrets.mutations.ensureProjectShareToken,
|
|
266
272
|
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id type; the runtime value is a plain string the server validates.
|
|
267
|
-
{
|
|
273
|
+
{ projectId: projectId }));
|
|
268
274
|
artifacts.shareSyncCommand = `npx -y @popoverai/dotrequirements@latest sync --share ${share.token}`;
|
|
269
275
|
}
|
|
270
276
|
catch {
|
|
@@ -273,9 +279,9 @@ async function mintShareArtifacts(client, teamId, projectId) {
|
|
|
273
279
|
return artifacts;
|
|
274
280
|
}
|
|
275
281
|
async function listProjects(client, teamId) {
|
|
276
|
-
const projects = (await client.query(api.projectSecrets.queries.
|
|
282
|
+
const projects = (await client.query(api.projectSecrets.queries.listOwnForTeam,
|
|
277
283
|
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id type; the runtime value is a plain string the server validates.
|
|
278
|
-
{
|
|
284
|
+
{ teamId: teamId }));
|
|
279
285
|
return projects.map((p) => ({
|
|
280
286
|
projectId: p.projectId,
|
|
281
287
|
projectName: p.projectName,
|
|
@@ -349,7 +355,7 @@ async function runInteractiveLink(options) {
|
|
|
349
355
|
return;
|
|
350
356
|
}
|
|
351
357
|
// Set up authenticated client using the session token
|
|
352
|
-
const client = new ConvexHttpClient(
|
|
358
|
+
const client = new ConvexHttpClient(convexUrl());
|
|
353
359
|
client.setAuth(oauthResult.accessToken);
|
|
354
360
|
// LINK-4: Select team
|
|
355
361
|
const selectedTeam = await selectTeam(client);
|
|
@@ -417,7 +423,7 @@ async function runInteractiveLink(options) {
|
|
|
417
423
|
console.log("\nDocumentation: https://docs.dotrequirements.io/getting-started");
|
|
418
424
|
}
|
|
419
425
|
catch (error) {
|
|
420
|
-
console.error(`\n✗ Error: ${
|
|
426
|
+
console.error(`\n✗ Error: ${commandErrorMessage(error)}`);
|
|
421
427
|
// LINK-8: Network error guidance
|
|
422
428
|
if (error instanceof Error && error.message.includes("fetch")) {
|
|
423
429
|
console.error("\nCheck your internet connection and try again.");
|
package/dist/commands/report.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { convexUrl } from "../cloud.js";
|
|
1
2
|
import { findRequirementsDir, readCoverageCache } from "../harness/cache.js";
|
|
2
3
|
import { formatUnrecordedNotice } from "../harness/reportingStatus.js";
|
|
3
4
|
import { getProjectCoverage, getRequirementCoverage, } from "../requirements/cloud-coverage.js";
|
|
4
5
|
import { buildLocalReport, } from "../requirements/coverage.js";
|
|
5
6
|
import { findProjectRoot, getProjectCredentials, } from "../utils/project-settings.js";
|
|
6
|
-
|
|
7
|
+
import { markVersionMessagesShown, versionMessages, } from "../version-messages.js";
|
|
7
8
|
export async function reportCommand(options) {
|
|
8
9
|
const source = options.source ?? "local";
|
|
9
10
|
const format = options.format ?? "console";
|
|
@@ -52,24 +53,28 @@ export async function reportCommand(options) {
|
|
|
52
53
|
if (options.requirement) {
|
|
53
54
|
// REPORT-CLOUD-1.7: --branch/--since apply to the requirement-scoped
|
|
54
55
|
// output too, not just the project-wide report
|
|
55
|
-
const record = await getRequirementCoverage(options.requirement, projectId, projectSecret,
|
|
56
|
+
const record = await getRequirementCoverage(options.requirement, projectId, projectSecret, convexUrl(), {
|
|
56
57
|
branch: options.branch,
|
|
57
58
|
sinceTimestamp: options.since,
|
|
58
59
|
});
|
|
59
60
|
console.log(printCloudRequirement(record, format, {
|
|
60
61
|
branch: options.branch,
|
|
61
62
|
since: options.since,
|
|
62
|
-
}));
|
|
63
|
+
}, format === "json" ? await versionMessages() : []));
|
|
64
|
+
if (format === "json")
|
|
65
|
+
markVersionMessagesShown();
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
|
-
const record = await getProjectCoverage(projectId, projectSecret,
|
|
68
|
+
const record = await getProjectCoverage(projectId, projectSecret, convexUrl(), {
|
|
66
69
|
branch: options.branch,
|
|
67
70
|
sinceTimestamp: options.since,
|
|
68
71
|
});
|
|
69
72
|
console.log(printCloudProject(record, format, {
|
|
70
73
|
branch: options.branch,
|
|
71
74
|
since: options.since,
|
|
72
|
-
}));
|
|
75
|
+
}, format === "json" ? await versionMessages() : []));
|
|
76
|
+
if (format === "json")
|
|
77
|
+
markVersionMessagesShown();
|
|
73
78
|
}
|
|
74
79
|
function printLocalReport(report, format) {
|
|
75
80
|
switch (format) {
|
|
@@ -122,9 +127,10 @@ function formatLocalMarkdown(report) {
|
|
|
122
127
|
}
|
|
123
128
|
return out;
|
|
124
129
|
}
|
|
125
|
-
function printCloudRequirement(record, format, filters) {
|
|
130
|
+
function printCloudRequirement(record, format, filters, messages = []) {
|
|
126
131
|
if (format === "json") {
|
|
127
|
-
|
|
132
|
+
// CLI-MSG-1.1: the cloud's messages for this version travel in the JSON.
|
|
133
|
+
return JSON.stringify({ source: "cloud", requirement: record, messages }, null, 2);
|
|
128
134
|
}
|
|
129
135
|
if (!record.lastTestedAt) {
|
|
130
136
|
// REPORT-CLOUD-1.7: with filters in play, an empty record means nothing
|
|
@@ -168,9 +174,16 @@ function printCloudRequirement(record, format, filters) {
|
|
|
168
174
|
}
|
|
169
175
|
return lines.join("\n");
|
|
170
176
|
}
|
|
171
|
-
function printCloudProject(record, format, filters) {
|
|
177
|
+
function printCloudProject(record, format, filters, messages = []) {
|
|
172
178
|
if (format === "json") {
|
|
173
|
-
|
|
179
|
+
// CLI-MSG-1.1: the cloud's messages for this version travel in the JSON.
|
|
180
|
+
return JSON.stringify({
|
|
181
|
+
source: "cloud",
|
|
182
|
+
filters,
|
|
183
|
+
summary: cloudSummary(record),
|
|
184
|
+
...record,
|
|
185
|
+
messages,
|
|
186
|
+
}, null, 2);
|
|
174
187
|
}
|
|
175
188
|
const summary = cloudSummary(record);
|
|
176
189
|
const filterNote = formatCloudFilters(filters);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { webApiUrl } from "../cloud.js";
|
|
4
|
+
import { fetchReviewTestFeedback } from "../requirements/cloud-ai.js";
|
|
4
5
|
import { findRequirementsInFile } from "../requirements/grep.js";
|
|
5
6
|
import { formatRequirementTree, getRequirementTree, loadAllRequirements, } from "../requirements/index.js";
|
|
6
7
|
import { getProjectCredentials } from "../utils/project-settings.js";
|
|
@@ -145,7 +146,7 @@ async function runCloudReview(params) {
|
|
|
145
146
|
`(${error instanceof Error ? error.message : String(error)})`);
|
|
146
147
|
}
|
|
147
148
|
// CLI-REVIEW-3.0 / .1 / .3: dispatch to the hosted endpoint
|
|
148
|
-
const apiBaseUrl =
|
|
149
|
+
const apiBaseUrl = webApiUrl();
|
|
149
150
|
let feedback;
|
|
150
151
|
let quotaWarning;
|
|
151
152
|
try {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { convexUrl, webApiUrl } from "../cloud.js";
|
|
4
|
+
import { fetchStyleCheckFeedback, getProjectContext, } from "../requirements/cloud-ai.js";
|
|
4
5
|
import { filterRequirementsByKeys, loadAllRequirements, } from "../requirements/index.js";
|
|
5
6
|
import { generateStyleGuideBody } from "../requirements/style-guide.js";
|
|
6
7
|
import { readLocalStyleGuide } from "../requirements/style-guide-file.js";
|
|
7
8
|
import { findProjectRoot, getProjectCredentials, } from "../utils/project-settings.js";
|
|
8
|
-
const CONVEX_URL = "https://data.dotrequirements.io";
|
|
9
9
|
/**
|
|
10
10
|
* Judgment instructions for test-file style review in local mode. Mirrors the
|
|
11
11
|
* hosted test-style rubric (PROMPT-STYLE-TESTS-*): requirement() usage,
|
|
@@ -123,7 +123,7 @@ async function emitStyleMaterials(params) {
|
|
|
123
123
|
if (projectRoot) {
|
|
124
124
|
try {
|
|
125
125
|
const { projectId, projectSecret } = getProjectCredentials(workspaceRoot);
|
|
126
|
-
const contextData = await getProjectContext(projectId, projectSecret,
|
|
126
|
+
const contextData = await getProjectContext(projectId, projectSecret, convexUrl());
|
|
127
127
|
customStyleGuidance = contextData?.requirementsStyleContext ?? null;
|
|
128
128
|
}
|
|
129
129
|
catch {
|
|
@@ -170,7 +170,7 @@ async function runCloudStyleCheck(params) {
|
|
|
170
170
|
`(${error instanceof Error ? error.message : String(error)})`);
|
|
171
171
|
}
|
|
172
172
|
// CLI-STYLE-3.0 / .1 / .3: dispatch to the hosted endpoint
|
|
173
|
-
const apiBaseUrl =
|
|
173
|
+
const apiBaseUrl = webApiUrl();
|
|
174
174
|
let feedback;
|
|
175
175
|
let quotaWarning;
|
|
176
176
|
try {
|
package/dist/convex.d.ts
CHANGED
|
@@ -17,15 +17,13 @@
|
|
|
17
17
|
export declare const api: {
|
|
18
18
|
documents: {
|
|
19
19
|
queries: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
exportDocument: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
21
|
+
exportProject: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
22
|
+
exportProjectForShare: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
23
23
|
};
|
|
24
24
|
mutations: {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
deleteForCli: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
28
|
-
setAttachmentsForSync: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
25
|
+
deleteForSync: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
26
|
+
syncAttachments: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
29
27
|
};
|
|
30
28
|
saveWithRequirements: {
|
|
31
29
|
saveWithRequirements: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
@@ -39,7 +37,7 @@ export declare const api: {
|
|
|
39
37
|
projects: {
|
|
40
38
|
mutations: {
|
|
41
39
|
create: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
42
|
-
|
|
40
|
+
updateProject: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
43
41
|
};
|
|
44
42
|
};
|
|
45
43
|
teams: {
|
|
@@ -55,19 +53,19 @@ export declare const api: {
|
|
|
55
53
|
getInviteInfo: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
56
54
|
};
|
|
57
55
|
mutations: {
|
|
58
|
-
|
|
56
|
+
createInviteLink: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
59
57
|
acceptInvite: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
60
58
|
};
|
|
61
59
|
};
|
|
62
60
|
projectSecrets: {
|
|
63
61
|
queries: {
|
|
64
62
|
getOwnSecret: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
65
|
-
|
|
63
|
+
listOwnForTeam: import("convex/server").FunctionReference<"query", "public", any, any, string | undefined>;
|
|
66
64
|
};
|
|
67
65
|
mutations: {
|
|
68
|
-
|
|
66
|
+
ensureOwn: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
69
67
|
revokeOwnSecret: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
70
|
-
|
|
68
|
+
ensureProjectShareToken: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
|
|
71
69
|
};
|
|
72
70
|
};
|
|
73
71
|
lib: {
|
package/dist/convex.js
CHANGED
|
@@ -21,17 +21,16 @@ const action = (name) => makeFunctionReference(name);
|
|
|
21
21
|
export const api = {
|
|
22
22
|
documents: {
|
|
23
23
|
queries: {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
exportDocument: query("documents/queries:exportDocument"),
|
|
25
|
+
exportProject: query("documents/queries:exportProject"),
|
|
26
|
+
// SHARE-TOKEN-3: the same export for a read-only share token
|
|
27
|
+
exportProjectForShare: query("documents/queries:exportProjectForShare"),
|
|
27
28
|
},
|
|
28
29
|
mutations: {
|
|
29
|
-
create: mutation("documents/mutations:create"),
|
|
30
|
-
update: mutation("documents/mutations:update"),
|
|
31
30
|
// SYNC-MODE-3: delete a cloud document from the CLI (repo-wins)
|
|
32
|
-
|
|
31
|
+
deleteForSync: mutation("documents/mutations:deleteForSync"),
|
|
33
32
|
// ATTACH-10.2: attachment-only sync — a metadata patch, never a publish
|
|
34
|
-
|
|
33
|
+
syncAttachments: mutation("documents/mutations:syncAttachments"),
|
|
35
34
|
},
|
|
36
35
|
// SYNC-ARCH-1: Save document with requirements derivation
|
|
37
36
|
saveWithRequirements: {
|
|
@@ -46,7 +45,7 @@ export const api = {
|
|
|
46
45
|
projects: {
|
|
47
46
|
mutations: {
|
|
48
47
|
create: mutation("projects/mutations:create"),
|
|
49
|
-
|
|
48
|
+
updateProject: mutation("projects/mutations:updateProject"),
|
|
50
49
|
},
|
|
51
50
|
},
|
|
52
51
|
teams: {
|
|
@@ -62,7 +61,7 @@ export const api = {
|
|
|
62
61
|
getInviteInfo: query("teamInvites/queries:getInviteInfo"),
|
|
63
62
|
},
|
|
64
63
|
mutations: {
|
|
65
|
-
|
|
64
|
+
createInviteLink: mutation("teamInvites/mutations:createInviteLink"),
|
|
66
65
|
// Joins the team and reconciles paid-team seats via a scheduled sync
|
|
67
66
|
// (POLAR-SEATS-3). Replaced the deleted polar:acceptTeamInvite action.
|
|
68
67
|
acceptInvite: mutation("teamInvites/mutations:acceptInvite"),
|
|
@@ -71,12 +70,12 @@ export const api = {
|
|
|
71
70
|
projectSecrets: {
|
|
72
71
|
queries: {
|
|
73
72
|
getOwnSecret: query("projectSecrets/queries:getOwnSecret"),
|
|
74
|
-
|
|
73
|
+
listOwnForTeam: query("projectSecrets/queries:listOwnForTeam"),
|
|
75
74
|
},
|
|
76
75
|
mutations: {
|
|
77
|
-
|
|
76
|
+
ensureOwn: mutation("projectSecrets/mutations:ensureOwn"),
|
|
78
77
|
revokeOwnSecret: mutation("projectSecrets/mutations:revokeOwnSecret"),
|
|
79
|
-
|
|
78
|
+
ensureProjectShareToken: mutation("projectSecrets/mutations:ensureProjectShareToken"),
|
|
80
79
|
},
|
|
81
80
|
},
|
|
82
81
|
lib: {
|
package/dist/harness/finalize.js
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { execSync } from "node:child_process";
|
|
13
13
|
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { convexUrl } from "../cloud.js";
|
|
14
15
|
import { canonicalRequirementPath } from "../schema/index.js";
|
|
15
16
|
import { getProjectInfo } from "../utils/project-settings.js";
|
|
17
|
+
import { enableVersionMessages, formatVersionMessages, markVersionMessagesShown, versionMessages, } from "../version-messages.js";
|
|
16
18
|
import { cleanupTestRunId, clearCoverageReportFailure, deleteTrackingFile, findProjectRoot, findRequirementsDir, getTestRunId, needsReporting, readCoverageCache, readLookupCache, readTrackingEntries, recordCoverageReplayed, recordCoverageReported, recordCoverageReportFailure, updateCoverageCache, } from "./cache.js";
|
|
17
19
|
import { formatFailureNotice, formatUnrecordedNotice, refusalReason, } from "./reportingStatus.js";
|
|
18
20
|
import { toProjectRelativePath } from "./tracking.js";
|
|
@@ -130,10 +132,6 @@ function printLocalReport(testedKeys, lookup, options) {
|
|
|
130
132
|
console.log(report);
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Production Convex deployment URL
|
|
135
|
-
*/
|
|
136
|
-
const CONVEX_URL = "https://data.dotrequirements.io";
|
|
137
135
|
/**
|
|
138
136
|
* Record a reporting attempt that did not reach the cloud, so later runs and
|
|
139
137
|
* `dotreq report` keep saying so (COVERAGE-REPORT-3), and hand the reason back
|
|
@@ -190,18 +188,18 @@ const REPORT_TIMEOUT_MS = 15_000;
|
|
|
190
188
|
*/
|
|
191
189
|
async function postCoverage(credentials, report) {
|
|
192
190
|
// Note: projectId from env is always a slug (e.g., "reduced-cephalopod-288"),
|
|
193
|
-
// not a Convex ID.
|
|
194
|
-
const response = await fetch(`${
|
|
191
|
+
// not a Convex ID. It names the project both to authenticate and as the report's project.
|
|
192
|
+
const response = await fetch(`${convexUrl()}/api/mutation`, {
|
|
195
193
|
method: "POST",
|
|
196
194
|
headers: { "Content-Type": "application/json" },
|
|
197
195
|
body: JSON.stringify({
|
|
198
|
-
path: "testCoverage/mutations:
|
|
196
|
+
path: "testCoverage/mutations:record",
|
|
199
197
|
args: {
|
|
200
198
|
projectAuth: {
|
|
201
199
|
projectSlug: credentials.projectId,
|
|
202
200
|
projectSecret: credentials.projectSecret,
|
|
203
201
|
},
|
|
204
|
-
|
|
202
|
+
project: credentials.projectId,
|
|
205
203
|
branch: report.branch,
|
|
206
204
|
context: report.context,
|
|
207
205
|
runId: report.runId,
|
|
@@ -528,6 +526,9 @@ export async function finalize(options = {}) {
|
|
|
528
526
|
count: 0,
|
|
529
527
|
};
|
|
530
528
|
if (shouldReportToCloud) {
|
|
529
|
+
// CLI-MSG-1.4: a run that reports to the cloud hears what it has to say
|
|
530
|
+
// to this version.
|
|
531
|
+
enableVersionMessages();
|
|
531
532
|
cloudResult = await reportToConvex(projectRoot, testRunId, aggregated, showCloudStatus, context);
|
|
532
533
|
}
|
|
533
534
|
// Read once, after reporting has had its say, for both the report header and
|
|
@@ -551,6 +552,13 @@ export async function finalize(options = {}) {
|
|
|
551
552
|
(recordedThisRun ? null : coverageCache?.lastFailure)),
|
|
552
553
|
});
|
|
553
554
|
}
|
|
555
|
+
// CLI-MSG-1.4: the summary closes with the cloud's messages for this
|
|
556
|
+
// version — ahead of the notice below, which keeps the last word.
|
|
557
|
+
const messages = await versionMessages();
|
|
558
|
+
if (messages.length > 0) {
|
|
559
|
+
console.error(`${formatVersionMessages(messages)}\n`);
|
|
560
|
+
}
|
|
561
|
+
markVersionMessagesShown();
|
|
554
562
|
// COVERAGE-REPORT-2.1, -3.0: the run's last word is whether coverage
|
|
555
563
|
// recorded. Written to stderr so it cannot be silenced by a quiet run or
|
|
556
564
|
// swallowed by a caller parsing stdout — a failure nobody sees is the whole
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* COVERAGE-REPORT-2 / -3 (the run says coverage did not record, and keeps
|
|
6
6
|
* saying it until it does).
|
|
7
7
|
*/
|
|
8
|
+
import { userFacingMessage } from "../utils/command-error.js";
|
|
8
9
|
/**
|
|
9
10
|
* Shown when a refusal carries nothing a developer could act on
|
|
10
11
|
* (COVERAGE-REPORT-1.2). The raw `errorMessage` is deliberately not used in its
|
|
@@ -19,17 +20,7 @@ export const REFUSAL_WITHOUT_REASON = "the cloud refused the report without givi
|
|
|
19
20
|
* ConvexError's data is either a plain string or an object carrying `message`.
|
|
20
21
|
*/
|
|
21
22
|
export function refusalReason(response) {
|
|
22
|
-
|
|
23
|
-
if (typeof data === "string" && data.trim()) {
|
|
24
|
-
return data;
|
|
25
|
-
}
|
|
26
|
-
if (data && typeof data === "object" && "message" in data) {
|
|
27
|
-
const { message } = data;
|
|
28
|
-
if (typeof message === "string" && message.trim()) {
|
|
29
|
-
return message;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return REFUSAL_WITHOUT_REASON;
|
|
23
|
+
return userFacingMessage(response.errorData) ?? REFUSAL_WITHOUT_REASON;
|
|
33
24
|
}
|
|
34
25
|
const MONTHS = [
|
|
35
26
|
"January",
|
|
@@ -7,12 +7,6 @@
|
|
|
7
7
|
* throws on failure). Surface-specific concerns (error formatting, exit
|
|
8
8
|
* codes, MCP tool responses) live in the callers.
|
|
9
9
|
*/
|
|
10
|
-
/**
|
|
11
|
-
* Default base URL for the hosted AI endpoints (`/api/style-check`,
|
|
12
|
-
* `/api/review-test`). Overridable via the `apiBaseUrl` argument or
|
|
13
|
-
* the `DOTREQUIREMENTS_API_URL` environment variable in callers.
|
|
14
|
-
*/
|
|
15
|
-
export declare const DEFAULT_API_BASE_URL = "https://app.dotrequirements.io";
|
|
16
10
|
export interface StyleCheckParams {
|
|
17
11
|
apiBaseUrl: string;
|
|
18
12
|
projectId: string;
|
|
@@ -7,12 +7,6 @@
|
|
|
7
7
|
* throws on failure). Surface-specific concerns (error formatting, exit
|
|
8
8
|
* codes, MCP tool responses) live in the callers.
|
|
9
9
|
*/
|
|
10
|
-
/**
|
|
11
|
-
* Default base URL for the hosted AI endpoints (`/api/style-check`,
|
|
12
|
-
* `/api/review-test`). Overridable via the `apiBaseUrl` argument or
|
|
13
|
-
* the `DOTREQUIREMENTS_API_URL` environment variable in callers.
|
|
14
|
-
*/
|
|
15
|
-
export const DEFAULT_API_BASE_URL = "https://app.dotrequirements.io";
|
|
16
10
|
/**
|
|
17
11
|
* POST to `/api/style-check`. Returns the feedback (and any quota warning)
|
|
18
12
|
* on success; throws an Error with the underlying failure reason on a
|
|
@@ -73,16 +67,13 @@ export async function getProjectContext(projectId, projectSecret, convexUrl) {
|
|
|
73
67
|
method: "POST",
|
|
74
68
|
headers: { "Content-Type": "application/json" },
|
|
75
69
|
body: JSON.stringify({
|
|
76
|
-
path: "projects/queries:
|
|
70
|
+
path: "projects/queries:getProject",
|
|
77
71
|
args: {
|
|
78
72
|
projectAuth: {
|
|
79
73
|
projectSlug: projectId,
|
|
80
74
|
projectSecret,
|
|
81
75
|
},
|
|
82
|
-
|
|
83
|
-
type: "project",
|
|
84
|
-
slug: projectId,
|
|
85
|
-
},
|
|
76
|
+
project: projectId,
|
|
86
77
|
},
|
|
87
78
|
format: "json",
|
|
88
79
|
}),
|
|
@@ -19,10 +19,10 @@ export async function getRequirementCoverage(requirementKey, projectId, projectS
|
|
|
19
19
|
method: "POST",
|
|
20
20
|
headers: { "Content-Type": "application/json" },
|
|
21
21
|
body: JSON.stringify({
|
|
22
|
-
path: "testCoverage/queries:
|
|
22
|
+
path: "testCoverage/queries:requirementCoverage",
|
|
23
23
|
args: {
|
|
24
24
|
projectAuth: { projectSlug: projectId, projectSecret },
|
|
25
|
-
|
|
25
|
+
project: projectId,
|
|
26
26
|
requirementKey,
|
|
27
27
|
},
|
|
28
28
|
format: "json",
|
|
@@ -68,10 +68,10 @@ export async function getProjectCoverage(projectId, projectSecret, convexUrl, op
|
|
|
68
68
|
method: "POST",
|
|
69
69
|
headers: { "Content-Type": "application/json" },
|
|
70
70
|
body: JSON.stringify({
|
|
71
|
-
path: "testCoverage/queries:
|
|
71
|
+
path: "testCoverage/queries:projectCoverage",
|
|
72
72
|
args: {
|
|
73
73
|
projectAuth: { projectSlug: projectId, projectSecret },
|
|
74
|
-
|
|
74
|
+
project: projectId,
|
|
75
75
|
branch: options?.branch,
|
|
76
76
|
sinceTimestamp: options?.sinceTimestamp,
|
|
77
77
|
},
|
package/dist/sync/execute.js
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import { ConvexHttpClient } from "convex/browser";
|
|
13
|
-
import {
|
|
13
|
+
import { convexUrl } from "../cloud.js";
|
|
14
14
|
import { api } from "../convex.js";
|
|
15
15
|
import { parseFilesForPushIndividually, WEB_APP_URL } from "../push/core.js";
|
|
16
16
|
import { composeMarkdownWithTitle, splitLeadingH1, writeRequirementsFile, } from "../schema/index.js";
|
|
17
|
+
import { commandErrorMessage } from "../utils/command-error.js";
|
|
17
18
|
import { cloudAttachmentRemovals, resolveAttachments } from "./attachments.js";
|
|
18
19
|
import { resolveLocalPath } from "./local-files.js";
|
|
19
20
|
import { publishToCloud } from "./publish.js";
|
|
@@ -46,7 +47,7 @@ function emptyOutcome() {
|
|
|
46
47
|
*/
|
|
47
48
|
export async function executePlan(plan, cloud, auth, workspaceRoot, mode) {
|
|
48
49
|
const outcome = emptyOutcome();
|
|
49
|
-
const client = new ConvexHttpClient(
|
|
50
|
+
const client = new ConvexHttpClient(convexUrl());
|
|
50
51
|
const cloudById = new Map(cloud.documents.map((d) => [d.documentId, d]));
|
|
51
52
|
const requirementsDir = path.join(workspaceRoot, ".requirements");
|
|
52
53
|
const usedPaths = new Set();
|
|
@@ -83,14 +84,14 @@ export async function executePlan(plan, cloud, auth, workspaceRoot, mode) {
|
|
|
83
84
|
await syncResidualAttachments(client, auth, doc, action, outcome, mode);
|
|
84
85
|
}
|
|
85
86
|
catch (err) {
|
|
86
|
-
outcome.errors.push({ name, error:
|
|
87
|
+
outcome.errors.push({ name, error: commandErrorMessage(err) });
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
return outcome;
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
93
|
* Attachment-only operations for whatever the body action didn't cover.
|
|
93
|
-
* The push goes through
|
|
94
|
+
* The push goes through syncAttachments — a metadata patch, never the
|
|
94
95
|
* publish path, so naming a link cannot displace anyone's working document.
|
|
95
96
|
*/
|
|
96
97
|
async function syncResidualAttachments(client, auth, doc, action, outcome, mode) {
|
|
@@ -110,12 +111,11 @@ async function syncResidualAttachments(client, auth, doc, action, outcome, mode)
|
|
|
110
111
|
if (!auth.projectSlug) {
|
|
111
112
|
throw new Error("Cannot sync attachments without project credentials.");
|
|
112
113
|
}
|
|
113
|
-
await client.mutation(api.documents.mutations.
|
|
114
|
+
await client.mutation(api.documents.mutations.syncAttachments, {
|
|
114
115
|
projectAuth: {
|
|
115
116
|
projectSlug: auth.projectSlug,
|
|
116
117
|
projectSecret: auth.projectSecret,
|
|
117
118
|
},
|
|
118
|
-
target: { type: "project", slug: auth.projectSlug },
|
|
119
119
|
documentId: doc.documentId,
|
|
120
120
|
attachments: res.cloudFinal,
|
|
121
121
|
// Compare-and-set: the resolution was computed from the snapshot's
|
|
@@ -161,17 +161,6 @@ async function syncResidualAttachments(client, auth, doc, action, outcome, mode)
|
|
|
161
161
|
outcome.attachmentWrites++;
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
/**
|
|
165
|
-
* Servers throw ConvexError({kind, message}) for expected failures; production
|
|
166
|
-
* redacts the Error message to "Server Error" but preserves error.data.
|
|
167
|
-
* Surface the human-readable message rather than the blob.
|
|
168
|
-
*/
|
|
169
|
-
function errorDisplayMessage(err) {
|
|
170
|
-
const data = err.data;
|
|
171
|
-
if (data && typeof data.message === "string")
|
|
172
|
-
return data.message;
|
|
173
|
-
return err instanceof Error ? err.message : String(err);
|
|
174
|
-
}
|
|
175
164
|
async function cloudWrite(auth, doc, cloudById, outcome, mode) {
|
|
176
165
|
if (!auth.projectSlug || !doc.filePath) {
|
|
177
166
|
throw new Error("Cannot write to the cloud without project credentials.");
|
|
@@ -361,12 +350,11 @@ async function cloudDelete(client, auth, doc, outcome) {
|
|
|
361
350
|
if (!auth.projectSlug || !doc.documentId) {
|
|
362
351
|
throw new Error("Cannot delete from the cloud without project credentials.");
|
|
363
352
|
}
|
|
364
|
-
await client.mutation(api.documents.mutations.
|
|
353
|
+
await client.mutation(api.documents.mutations.deleteForSync, {
|
|
365
354
|
projectAuth: {
|
|
366
355
|
projectSlug: auth.projectSlug,
|
|
367
356
|
projectSecret: auth.projectSecret,
|
|
368
357
|
},
|
|
369
|
-
target: { type: "project", slug: auth.projectSlug },
|
|
370
358
|
documentId: doc.documentId,
|
|
371
359
|
});
|
|
372
360
|
outcome.cloudDeleted++;
|
package/dist/sync/publish.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* repo unable to push. Recoverable rather than lossy — see
|
|
12
12
|
* docs/working/publishing-and-unpublished-changes.md.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { webApiUrl } from "../cloud.js";
|
|
15
15
|
/**
|
|
16
16
|
* POST to `/api/publish`. Returns the saved document; throws with the server's
|
|
17
17
|
* own message on failure, which is what Jaime reads in the sync output.
|
|
@@ -21,7 +21,7 @@ import { DEFAULT_API_BASE_URL } from "../requirements/cloud-ai.js";
|
|
|
21
21
|
* apply next time.
|
|
22
22
|
*/
|
|
23
23
|
export async function publishToCloud(params) {
|
|
24
|
-
const apiBaseUrl =
|
|
24
|
+
const apiBaseUrl = webApiUrl();
|
|
25
25
|
const response = await fetch(`${apiBaseUrl}/api/publish`, {
|
|
26
26
|
method: "POST",
|
|
27
27
|
headers: { "Content-Type": "application/json" },
|
package/dist/sync/snapshot.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Snapshot acquisition for the comparator — kept separate from comparison so
|
|
3
3
|
* the comparator stays a pure function (compare.ts). The local half reads the
|
|
4
|
-
* workspace; the cloud half is one
|
|
4
|
+
* workspace; the cloud half is one export query.
|
|
5
5
|
*/
|
|
6
6
|
import type { CloudSnapshot, LocalDocument, LocalSnapshot } from "./types.js";
|
|
7
7
|
/**
|
|
@@ -22,9 +22,9 @@ export interface CloudAuth {
|
|
|
22
22
|
projectSecret: string;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* Build the cloud snapshot with one
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* Build the cloud snapshot with one export query: the project's own, with a
|
|
26
|
+
* project secret, or the share endpoint, with a bare share token (the token
|
|
27
|
+
* names the project) — the read-only path never writes back.
|
|
28
28
|
*/
|
|
29
29
|
export declare function acquireCloudSnapshot(auth: CloudAuth): Promise<CloudSnapshot>;
|
|
30
30
|
//# sourceMappingURL=snapshot.d.ts.map
|
package/dist/sync/snapshot.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Snapshot acquisition for the comparator — kept separate from comparison so
|
|
3
3
|
* the comparator stays a pure function (compare.ts). The local half reads the
|
|
4
|
-
* workspace; the cloud half is one
|
|
4
|
+
* workspace; the cloud half is one export query.
|
|
5
5
|
*/
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import { ConvexHttpClient } from "convex/browser";
|
|
8
8
|
import YAML from "yaml";
|
|
9
|
-
import {
|
|
9
|
+
import { convexUrl } from "../cloud.js";
|
|
10
10
|
import { api } from "../convex.js";
|
|
11
11
|
import { findRequirementsFiles } from "../requirements/index.js";
|
|
12
12
|
import { splitLeadingH1, validateMetadata } from "../schema/index.js";
|
|
@@ -120,18 +120,23 @@ export async function acquireLocalSnapshot(workspaceRoot, filePaths) {
|
|
|
120
120
|
return { documents: paths.map(readLocalDocument) };
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
|
-
* Build the cloud snapshot with one
|
|
124
|
-
*
|
|
125
|
-
*
|
|
123
|
+
* Build the cloud snapshot with one export query: the project's own, with a
|
|
124
|
+
* project secret, or the share endpoint, with a bare share token (the token
|
|
125
|
+
* names the project) — the read-only path never writes back.
|
|
126
126
|
*/
|
|
127
127
|
export async function acquireCloudSnapshot(auth) {
|
|
128
|
-
const client = new ConvexHttpClient(
|
|
129
|
-
const
|
|
130
|
-
?
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
const client = new ConvexHttpClient(convexUrl());
|
|
129
|
+
const result = (await (auth.projectSlug
|
|
130
|
+
? client.query(api.documents.queries.exportProject, {
|
|
131
|
+
projectAuth: {
|
|
132
|
+
projectSlug: auth.projectSlug,
|
|
133
|
+
projectSecret: auth.projectSecret,
|
|
134
|
+
},
|
|
135
|
+
project: auth.projectSlug,
|
|
136
|
+
})
|
|
137
|
+
: client.query(api.documents.queries.exportProjectForShare, {
|
|
138
|
+
shareToken: auth.projectSecret,
|
|
139
|
+
})));
|
|
135
140
|
return {
|
|
136
141
|
projectName: result.projectName,
|
|
137
142
|
projectSlug: result.projectSlug,
|
package/dist/sync/types.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface LocalDocument {
|
|
|
34
34
|
parseError?: string;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
* One published document as it exists in the cloud, from
|
|
37
|
+
* One published document as it exists in the cloud, from the project export.
|
|
38
38
|
*/
|
|
39
39
|
export interface CloudDocument {
|
|
40
40
|
documentId: string;
|
|
@@ -26,8 +26,8 @@ HOWTO-1: How to use dotrequirements
|
|
|
26
26
|
2.0. Nesting → You can keep nesting. Label references don't need to be nested: requirement('HOWTO-1.nesting').
|
|
27
27
|
2.1. Several words → Reference multi-word labels using kebab-case. Label references are never case sensitive: requirement('HOWTO-1.several-words')
|
|
28
28
|
3. → While you _can_ write your requirements longhand in this format, there are a number of better ways.
|
|
29
|
-
3.0.
|
|
30
|
-
3.1.
|
|
29
|
+
3.0. Coding assistants → Run \`dotreq ai-setup\` and your coding assistant develops requirements with you
|
|
30
|
+
3.1. Chat → Connect Claude or ChatGPT to dot•requirements and write specs in the conversation
|
|
31
31
|
3.2. Our composer → You can use our requirements composer at app.dotrequirements.io
|
|
32
32
|
4. Have fun → Especially in the time you get back from having a more stable, transparent, and AI-friendly codebase
|
|
33
33
|
\`\`\`
|
|
@@ -29,8 +29,8 @@ HOWTO-1: How to use dotrequirements
|
|
|
29
29
|
2.0. Nesting → You can keep nesting. Label references don't need to be nested: requirement('HOWTO-1.nesting').
|
|
30
30
|
2.1. Several words → Reference multi-word labels using kebab-case. Label references are never case sensitive: requirement('HOWTO-1.several-words')
|
|
31
31
|
3. → While you _can_ write your requirements longhand in this format, there are a number of better ways.
|
|
32
|
-
3.0.
|
|
33
|
-
3.1.
|
|
32
|
+
3.0. Coding assistants → Run \`dotreq ai-setup\` and your coding assistant develops requirements with you
|
|
33
|
+
3.1. Chat → Connect Claude or ChatGPT to dot•requirements and write specs in the conversation
|
|
34
34
|
3.2. Our composer → You can use our requirements composer at app.dotrequirements.io
|
|
35
35
|
4. Have fun → Especially in the time you get back from having a more stable, transparent, and AI-friendly codebase
|
|
36
36
|
\`\`\`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a failed command tells Sam (ERR-6).
|
|
3
|
+
*
|
|
4
|
+
* Production Convex replaces the message of every server error with
|
|
5
|
+
* "[Request ID: …] Server Error" and keeps only a ConvexError's `data`, which
|
|
6
|
+
* is where the backend puts the sentence written for users. So a refusal is
|
|
7
|
+
* read from its data; a server failure that carries none becomes a fixed
|
|
8
|
+
* sentence with its request ID; anything else is an error raised on Sam's
|
|
9
|
+
* machine, whose own message is already meant to be read.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The user-facing message a ConvexError's data carries, if any: the data is
|
|
13
|
+
* either a plain string or an object with a `message`. Mirrors
|
|
14
|
+
* `packages/web/lib/convex-error.ts`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function userFacingMessage(data: unknown): string | undefined;
|
|
17
|
+
/** The text to print for an error that ended a command. */
|
|
18
|
+
export declare function commandErrorMessage(error: unknown): string;
|
|
19
|
+
//# sourceMappingURL=command-error.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a failed command tells Sam (ERR-6).
|
|
3
|
+
*
|
|
4
|
+
* Production Convex replaces the message of every server error with
|
|
5
|
+
* "[Request ID: …] Server Error" and keeps only a ConvexError's `data`, which
|
|
6
|
+
* is where the backend puts the sentence written for users. So a refusal is
|
|
7
|
+
* read from its data; a server failure that carries none becomes a fixed
|
|
8
|
+
* sentence with its request ID; anything else is an error raised on Sam's
|
|
9
|
+
* machine, whose own message is already meant to be read.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The user-facing message a ConvexError's data carries, if any: the data is
|
|
13
|
+
* either a plain string or an object with a `message`. Mirrors
|
|
14
|
+
* `packages/web/lib/convex-error.ts`.
|
|
15
|
+
*/
|
|
16
|
+
export function userFacingMessage(data) {
|
|
17
|
+
if (typeof data === "string" && data.trim())
|
|
18
|
+
return data;
|
|
19
|
+
if (data && typeof data === "object" && "message" in data) {
|
|
20
|
+
const { message } = data;
|
|
21
|
+
if (typeof message === "string" && message.trim())
|
|
22
|
+
return message;
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const SERVER_ERROR = /\bServer Error\b/;
|
|
27
|
+
const REQUEST_ID = /Request ID: ([\w-]+)/;
|
|
28
|
+
/** The text to print for an error that ended a command. */
|
|
29
|
+
export function commandErrorMessage(error) {
|
|
30
|
+
if (error && typeof error === "object" && "data" in error) {
|
|
31
|
+
const message = userFacingMessage(error.data);
|
|
32
|
+
if (message)
|
|
33
|
+
return message;
|
|
34
|
+
}
|
|
35
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
36
|
+
if (SERVER_ERROR.test(raw)) {
|
|
37
|
+
const requestId = REQUEST_ID.exec(raw)?.[1];
|
|
38
|
+
return requestId
|
|
39
|
+
? `Something went wrong in dotrequirements' cloud (request ID ${requestId}). Try again, and if it keeps happening, email support@dotrequirements.io with the request ID.`
|
|
40
|
+
: "Something went wrong in dotrequirements' cloud. Try again, and if it keeps happening, email support@dotrequirements.io.";
|
|
41
|
+
}
|
|
42
|
+
return raw;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=command-error.js.map
|
package/dist/utils/oauth-flow.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { randomBytes } from "node:crypto";
|
|
8
8
|
import { ConvexHttpClient } from "convex/browser";
|
|
9
|
-
import {
|
|
9
|
+
import { convexUrl } from "../cloud.js";
|
|
10
10
|
import { api } from "../convex.js";
|
|
11
11
|
import { openBrowserWithFallback } from "./browser-launch.js";
|
|
12
12
|
import { startCallbackServer } from "./oauth-callback-server.js";
|
|
@@ -58,7 +58,7 @@ export async function executeOAuthFlow() {
|
|
|
58
58
|
}
|
|
59
59
|
console.log("Authentication successful, exchanging code for tokens...");
|
|
60
60
|
// Exchange authorization code for tokens via Convex action (server-side WorkOS call)
|
|
61
|
-
const client = new ConvexHttpClient(
|
|
61
|
+
const client = new ConvexHttpClient(convexUrl());
|
|
62
62
|
const result = await client.action(api.lib.authActions.exchangeAuthorizationCode, {
|
|
63
63
|
code,
|
|
64
64
|
codeVerifier,
|
|
@@ -43,9 +43,9 @@ export async function selectTeam(client) {
|
|
|
43
43
|
*/
|
|
44
44
|
export async function selectProject(client, teamId) {
|
|
45
45
|
console.log("\nFetching projects...\n");
|
|
46
|
-
const projectsWithSecrets = (await client.query(api.projectSecrets.queries.
|
|
46
|
+
const projectsWithSecrets = (await client.query(api.projectSecrets.queries.listOwnForTeam,
|
|
47
47
|
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id<"teams"> type; the runtime value is a plain string the convex server validates.
|
|
48
|
-
{
|
|
48
|
+
{ teamId: teamId }));
|
|
49
49
|
if (projectsWithSecrets.length === 0) {
|
|
50
50
|
console.log("No projects found in this team.");
|
|
51
51
|
console.log('Use "dotrequirements init" to create a new project.\n');
|
|
@@ -79,9 +79,9 @@ export async function selectProject(client, teamId) {
|
|
|
79
79
|
* Free tier always gets 30 days regardless of this parameter.
|
|
80
80
|
*/
|
|
81
81
|
export async function getOrCreateProjectSecret(client, projectId, expiryDays) {
|
|
82
|
-
const result = (await client.mutation(api.projectSecrets.mutations.
|
|
82
|
+
const result = (await client.mutation(api.projectSecrets.mutations.ensureOwn, {
|
|
83
83
|
// biome-ignore lint/suspicious/noExplicitAny: CLI workspace doesn't import convex's branded Id<"projects"> type; the runtime value is a plain string the convex server validates.
|
|
84
|
-
|
|
84
|
+
projectId: projectId,
|
|
85
85
|
expiryDays,
|
|
86
86
|
}));
|
|
87
87
|
return result;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the cloud has to say to this CLI version (CLI-MSG-1, CLI-MSG-3).
|
|
3
|
+
*
|
|
4
|
+
* Once enabled, the first cloud use in a run asks the cloud for this
|
|
5
|
+
* version's messages, alongside the command's own work. At the end the
|
|
6
|
+
* command takes them: text commands print them after their output, JSON
|
|
7
|
+
* commands carry them in their JSON. Asking never changes the command: a
|
|
8
|
+
* failed, unreadable or slow answer means no messages.
|
|
9
|
+
*/
|
|
10
|
+
/** How long a finished command waits for the cloud's answer (CLI-MSG-3.2). */
|
|
11
|
+
export declare const MESSAGE_WAIT_MS = 1000;
|
|
12
|
+
/** Ask the cloud for this version's messages on the run's first cloud use. */
|
|
13
|
+
export declare function enableVersionMessages(): void;
|
|
14
|
+
/**
|
|
15
|
+
* The messages for this version, waiting at most `waitMs` for the cloud's
|
|
16
|
+
* answer; empty when the run never reached the cloud or they were already
|
|
17
|
+
* shown. Asking twice waits once.
|
|
18
|
+
*/
|
|
19
|
+
export declare function versionMessages(waitMs?: number): Promise<string[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Record that this run's messages reached the user, once they are actually
|
|
22
|
+
* printed, so `wrapCommand` doesn't print them again after a JSON command
|
|
23
|
+
* carried them — and does print them if that command failed first.
|
|
24
|
+
*/
|
|
25
|
+
export declare function markVersionMessagesShown(): void;
|
|
26
|
+
/** The messages as text, for a command's human-readable output. */
|
|
27
|
+
export declare function formatVersionMessages(messages: string[]): string;
|
|
28
|
+
//# sourceMappingURL=version-messages.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the cloud has to say to this CLI version (CLI-MSG-1, CLI-MSG-3).
|
|
3
|
+
*
|
|
4
|
+
* Once enabled, the first cloud use in a run asks the cloud for this
|
|
5
|
+
* version's messages, alongside the command's own work. At the end the
|
|
6
|
+
* command takes them: text commands print them after their output, JSON
|
|
7
|
+
* commands carry them in their JSON. Asking never changes the command: a
|
|
8
|
+
* failed, unreadable or slow answer means no messages.
|
|
9
|
+
*/
|
|
10
|
+
import { convexUrl, whenCloudFirstUsed } from "./cloud.js";
|
|
11
|
+
import { getOwnPackage } from "./utils/own-package.js";
|
|
12
|
+
/** How long a finished command waits for the cloud's answer (CLI-MSG-3.2). */
|
|
13
|
+
export const MESSAGE_WAIT_MS = 1_000;
|
|
14
|
+
let pending = null;
|
|
15
|
+
let abort = null;
|
|
16
|
+
let shown = false;
|
|
17
|
+
async function askCloud(signal) {
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(`${convexUrl()}/api/query`, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { "Content-Type": "application/json" },
|
|
22
|
+
body: JSON.stringify({
|
|
23
|
+
path: "cliMessages/queries:forVersion",
|
|
24
|
+
args: { version: getOwnPackage().version },
|
|
25
|
+
format: "json",
|
|
26
|
+
}),
|
|
27
|
+
signal,
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok)
|
|
30
|
+
return [];
|
|
31
|
+
const body = (await response.json());
|
|
32
|
+
const messages = body.status === "success" ? body.value?.messages : null;
|
|
33
|
+
return Array.isArray(messages)
|
|
34
|
+
? messages.filter((m) => typeof m === "string" && !!m)
|
|
35
|
+
: [];
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Ask the cloud for this version's messages on the run's first cloud use. */
|
|
42
|
+
export function enableVersionMessages() {
|
|
43
|
+
whenCloudFirstUsed(() => {
|
|
44
|
+
if (pending)
|
|
45
|
+
return;
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
abort = controller;
|
|
48
|
+
// Asked just after the command's own first request goes out, so the
|
|
49
|
+
// command's work is never queued behind the question.
|
|
50
|
+
pending = new Promise((resolve) => {
|
|
51
|
+
queueMicrotask(() => resolve(askCloud(controller.signal)));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The messages for this version, waiting at most `waitMs` for the cloud's
|
|
57
|
+
* answer; empty when the run never reached the cloud or they were already
|
|
58
|
+
* shown. Asking twice waits once.
|
|
59
|
+
*/
|
|
60
|
+
export async function versionMessages(waitMs = MESSAGE_WAIT_MS) {
|
|
61
|
+
if (shown || !pending)
|
|
62
|
+
return [];
|
|
63
|
+
let timer;
|
|
64
|
+
const late = new Promise((resolve) => {
|
|
65
|
+
timer = setTimeout(() => resolve([]), waitMs);
|
|
66
|
+
});
|
|
67
|
+
const messages = await Promise.race([pending, late]);
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
// Whatever came back is the answer: a slow one must neither keep the
|
|
70
|
+
// process alive after the command nor be waited for a second time.
|
|
71
|
+
abort?.abort();
|
|
72
|
+
pending = Promise.resolve(messages);
|
|
73
|
+
return messages;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Record that this run's messages reached the user, once they are actually
|
|
77
|
+
* printed, so `wrapCommand` doesn't print them again after a JSON command
|
|
78
|
+
* carried them — and does print them if that command failed first.
|
|
79
|
+
*/
|
|
80
|
+
export function markVersionMessagesShown() {
|
|
81
|
+
shown = true;
|
|
82
|
+
}
|
|
83
|
+
/** The messages as text, for a command's human-readable output. */
|
|
84
|
+
export function formatVersionMessages(messages) {
|
|
85
|
+
return messages.map((message) => `\n${message}`).join("\n");
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=version-messages.js.map
|
package/package.json
CHANGED
package/dist/config.d.ts
DELETED
package/dist/config.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Production Convex deployment URL
|
|
3
|
-
* All users connect to the same backend
|
|
4
|
-
*/
|
|
5
|
-
const CONVEX_URL = "https://data.dotrequirements.io";
|
|
6
|
-
/**
|
|
7
|
-
* Get the Convex deployment URL
|
|
8
|
-
*/
|
|
9
|
-
export function getConvexUrl() {
|
|
10
|
-
return CONVEX_URL;
|
|
11
|
-
}
|
|
12
|
-
//# sourceMappingURL=config.js.map
|