diff --git a/src/__tests__/bot-commands.test.ts b/src/__tests__/bot-commands.test.ts
index 82e9ad4..bec991e 100644
--- a/src/__tests__/bot-commands.test.ts
+++ b/src/__tests__/bot-commands.test.ts
@@ -45,7 +45,9 @@ describe("bot messages", () => {
expect(message).toContain("https://example.com/preview/demo-app");
expect(message).not.toContain("Pairing Token:");
expect(message).toContain("1. Запусти bootstrap-команду локально:");
+ expect(message).toContain("Эту команду можно запускать повторно для этого проекта.");
expect(message).toContain("2. После bootstrap запусти:");
+ expect(message).toContain("pnpm run dev");
});
it("includes TMA and preview links in launchMessage", () => {
@@ -61,5 +63,6 @@ describe("bot messages", () => {
expect(message).toContain("Telegram Link:");
expect(message).toContain("Preview URL:");
expect(message).toContain("Tunnel Status:");
+ expect(message).toContain("pnpm run dev");
});
});
diff --git a/src/__tests__/projects.test.ts b/src/__tests__/projects.test.ts
index d80f72d..2c97ccf 100644
--- a/src/__tests__/projects.test.ts
+++ b/src/__tests__/projects.test.ts
@@ -49,10 +49,9 @@ describe("createProject", () => {
expect(result.project.title).toBe("Test App");
expect(result.project.status).toBe("draft");
expect(result.pairingToken.claimedAt).toBeNull();
+ expect(result.pairingToken.expiresAt).toBeNull();
expect(result.state.projects).toHaveLength(1);
expect(result.state.pairingTokens).toHaveLength(1);
- expect(new Date(result.pairingToken.expiresAt).getTime() - new Date(result.pairingToken.createdAt).getTime())
- .toBe(30 * 60 * 1000);
});
it("normalizes slug from title", () => {
diff --git a/src/bot/commands.ts b/src/bot/commands.ts
index cf79585..ef74f18 100644
--- a/src/bot/commands.ts
+++ b/src/bot/commands.ts
@@ -55,7 +55,7 @@ export function launchUsageMessage(): string {
export function createdMessage(
slug: string,
bootstrapCmd: string,
- pairingToken: string,
+ _pairingToken: string,
tmaUrl: string,
previewUrl: string,
telegramMiniAppUrl: string,
@@ -65,9 +65,10 @@ export function createdMessage(
"",
"1. Запусти bootstrap-команду локально:",
`${esc(bootstrapCmd)}`,
+ "Эту команду можно запускать повторно для этого проекта.",
"",
"2. После bootstrap запусти:",
- "npm run dev",
+ "pnpm run dev",
"",
"Ссылки:",
`Preview URL: ${renderLink(previewUrl)}`,
@@ -91,6 +92,6 @@ export function launchMessage(
`Telegram Link: ${renderLink(telegramMiniAppUrl)}`,
`Preview URL: ${renderLink(previewUrl)}`,
"",
- "Если preview офлайн, подними локально проект командой npm run dev.",
+ "Если preview офлайн, подними локально проект командой pnpm run dev.",
].join("\n");
}
diff --git a/src/projects.ts b/src/projects.ts
index f91a147..1b719e5 100644
--- a/src/projects.ts
+++ b/src/projects.ts
@@ -69,7 +69,7 @@ export function createProject(
token: makeId("pair"),
projectId,
createdAt: now,
- expiresAt: addMinutes(now, 30),
+ expiresAt: null,
claimedAt: null,
claimedDeviceId: null,
};
@@ -97,7 +97,9 @@ export function inspectPairingToken(
const project = state.projects.find((p) => p.id === tokenRecord.projectId) ?? null;
if (!project) return { error: "ProjectNotFound" };
if (tokenRecord.claimedAt) return { error: "TokenAlreadyClaimed" };
- if (Date.parse(tokenRecord.expiresAt) <= Date.now()) return { error: "TokenExpired" };
+ if (tokenRecord.expiresAt !== null && Date.parse(tokenRecord.expiresAt) <= Date.now()) {
+ return { error: "TokenExpired" };
+ }
return {
project,
@@ -135,7 +137,9 @@ export function claimPairingToken(
};
}
- if (Date.parse(tokenRecord.expiresAt) <= Date.now()) return { error: "TokenExpired" };
+ if (tokenRecord.expiresAt !== null && Date.parse(tokenRecord.expiresAt) <= Date.now()) {
+ return { error: "TokenExpired" };
+ }
const deviceId = makeId("device");
const deviceSecret = makeId("secret");
diff --git a/src/types.ts b/src/types.ts
index ccdd9c7..763b2ec 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -16,7 +16,7 @@ export interface PairingToken {
token: string;
projectId: string;
createdAt: string;
- expiresAt: string;
+ expiresAt: string | null;
claimedAt: string | null;
claimedDeviceId: string | null;
}