@taskforcehq/taskforce 0.3.300 → 0.3.302
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 +19 -0
- package/dist/Taskforce.module.css +26 -2
- package/dist/TaskforceCore.filter-persistence.test.js +1 -1
- package/dist/TaskforceCore.js +13 -24
- package/dist/TaskforceCore.test.js +167 -152
- package/dist/components/features/TaskSettings.js +14 -82
- package/dist/components/features/TaskSettings.test.js +33 -70
- package/dist/components/features/settings/TaxonomyLibraryManager.js +8 -2
- package/dist/components/features/settings/TaxonomyLibraryManager.test.js +1 -1
- package/dist/components/task/TaskActionHeader.js +6 -3
- package/dist/components/task/TaskCard.js +6 -3
- package/dist/components/task/TaskForm.js +2 -2
- package/dist/components/task/TaskForm.test.js +41 -2
- package/dist/components/ui/ReferenceBadgeButton.js +1 -1
- package/dist/components/views/PlansPage.js +9 -8
- package/dist/components/views/StandaloneLayout.js +490 -207
- package/dist/components/views/panels/FilterToolbar.d.ts +2 -0
- package/dist/components/views/panels/FilterToolbarControls.d.ts +1 -0
- package/dist/components/views/panels/FilterToolbarControls.js +1 -1
- package/dist/components/views/panels/PlanningDrawer.js +19 -2
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +43 -0
- package/dist/components/views/standalone/modals/AccountHubModal.js +22 -0
- package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.js +8 -0
- package/dist/components/views/standalone/modals/EditProfileModal.d.ts +26 -0
- package/dist/components/views/standalone/modals/EditProfileModal.js +10 -0
- package/dist/components/views/standalone/modals/HelpModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/HelpModal.js +8 -0
- package/dist/components/views/standalone/modals/ScheduleWarningModal.d.ts +13 -0
- package/dist/components/views/standalone/modals/ScheduleWarningModal.js +6 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +61 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.js +38 -0
- package/dist/components/views/standalone/modals/TeamManagementModal.d.ts +64 -0
- package/dist/components/views/standalone/modals/TeamManagementModal.js +10 -0
- package/dist/components/views/standalone/modals/types.d.ts +2 -0
- package/dist/components/views/standalone/modals/types.js +1 -0
- package/dist/components/views/teamManagementAccess.d.ts +1 -1
- package/dist/components/views/teamManagementAccess.js +2 -2
- package/dist/components/views/teamManagementAccess.test.js +3 -3
- package/dist/config/deployment.js +2 -2
- package/dist/config/deployment.test.js +5 -3
- package/dist/core/AiProfileService.js +7 -2
- package/dist/core/AiProfiles.test.js +19 -0
- package/dist/core/SyncReconciliationService.d.ts +2 -0
- package/dist/core/SyncReconciliationService.js +3 -0
- package/dist/core/TaskAuditTimeline.test.js +40 -0
- package/dist/core/Taskforce.d.ts +49 -0
- package/dist/core/Taskforce.ids.test.js +133 -0
- package/dist/core/Taskforce.js +241 -78
- package/dist/core/UserProfileAvatarDrafts.test.d.ts +1 -0
- package/dist/core/UserProfileAvatarDrafts.test.js +102 -0
- package/dist/core/taskReferencePolicy.d.ts +28 -0
- package/dist/core/taskReferencePolicy.js +189 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/hooks/tasks/useTaskFormActions.js +3 -0
- package/dist/hooks/useTaskData.js +16 -11
- package/dist/hooks/useTaskData.test.js +127 -0
- package/dist/hooks/useTaskforce.d.ts +5 -0
- package/dist/hooks/useTaskforce.js +63 -15
- package/dist/hooks/useTaskforce.sync-behavior.test.js +185 -6
- package/dist/mcp/aiProfileBootstrap.d.ts +29 -0
- package/dist/mcp/aiProfileBootstrap.js +80 -0
- package/dist/mcp/aiProfileBootstrap.test.d.ts +1 -0
- package/dist/mcp/aiProfileBootstrap.test.js +68 -0
- package/dist/mcp/clientIntegrations.d.ts +2 -8
- package/dist/mcp/clientIntegrations.js +21 -40
- package/dist/mcp/clientIntegrations.test.js +28 -2
- package/dist/mcp/clientProfileDefaults.d.ts +12 -0
- package/dist/mcp/clientProfileDefaults.js +37 -0
- package/dist/mcp/runtime.d.ts +10 -7
- package/dist/mcp/runtime.js +45 -23
- package/dist/mcp/runtime.test.js +56 -3
- package/dist/migrations/taskSchemaMigrations.js +53 -1
- package/dist/qaOpen.test.d.ts +1 -0
- package/dist/qaOpen.test.js +56 -0
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +15 -1
- package/dist/server/index.test.js +14 -1
- package/dist/server/routes/admin.js +25 -22
- package/dist/server/routes/auth.d.ts +5 -0
- package/dist/server/routes/auth.js +350 -16
- package/dist/server/routes/billing.js +11 -1
- package/dist/server/routes/billing.test.js +32 -2
- package/dist/server/routes/documents.js +6 -1
- package/dist/server/routes/shared.d.ts +1 -1
- package/dist/server/routes/shared.js +11 -3
- package/dist/server/routes/sync.integration.test.js +130 -0
- package/dist/server/routes/sync.js +226 -1924
- package/dist/server/routes/tasks.js +18 -4
- package/dist/server/routes.js +26 -19
- package/dist/server/routes.test.js +352 -6
- package/dist/shared/runtimeContract.d.ts +1 -1
- package/dist/sync/collaborationSyncPayload.d.ts +17 -0
- package/dist/sync/collaborationSyncPayload.js +131 -0
- package/dist/sync/collaborationSyncPayload.test.d.ts +1 -0
- package/dist/sync/collaborationSyncPayload.test.js +104 -0
- package/dist/sync/collaborationSyncState.d.ts +15 -0
- package/dist/sync/collaborationSyncState.js +60 -0
- package/dist/sync/contentSyncPayload.d.ts +45 -0
- package/dist/sync/contentSyncPayload.js +104 -0
- package/dist/sync/contentSyncPayload.test.d.ts +1 -0
- package/dist/sync/contentSyncPayload.test.js +104 -0
- package/dist/sync/contentSyncState.d.ts +88 -0
- package/dist/sync/contentSyncState.js +743 -0
- package/dist/sync/planningSyncPayload.d.ts +9 -0
- package/dist/sync/planningSyncPayload.js +82 -0
- package/dist/sync/planningSyncPayload.test.d.ts +1 -0
- package/dist/sync/planningSyncPayload.test.js +68 -0
- package/dist/sync/syncApplyHandlers.d.ts +83 -0
- package/dist/sync/syncApplyHandlers.js +445 -0
- package/dist/sync/taskSyncPayload.d.ts +33 -0
- package/dist/sync/taskSyncPayload.js +126 -0
- package/dist/sync/taskSyncPayload.test.d.ts +1 -0
- package/dist/sync/taskSyncPayload.test.js +54 -0
- package/dist/sync/workspacePullFeed.d.ts +28 -0
- package/dist/sync/workspacePullFeed.js +301 -0
- package/dist/sync/workspacePullFeed.test.d.ts +1 -0
- package/dist/sync/workspacePullFeed.test.js +166 -0
- package/dist/sync/workspaceSyncModel.d.ts +4 -1
- package/dist/sync/workspaceSyncModel.js +9 -2
- package/dist/sync/workspaceSyncModel.test.js +29 -0
- package/dist/test/setup.js +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/ui/assets/{AgentsModule-TVmCgizS.js → AgentsModule-JG5jc3he.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-CAyhnPeR.js → AnnotatedAttachmentWorkspace-Dvi02Y7q.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-BOCw5e4D.js → DocumentWorkspace-iys3zfca.js} +2 -2
- package/dist/ui/assets/{InitiativesModule-CN0r02Oi.js → InitiativesModule-DJvLJI3A.js} +1 -1
- package/dist/ui/assets/PlansPage-BMPlpFpw.js +1 -0
- package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +8 -0
- package/dist/ui/assets/{TaskSettings-BwB9NtmJ.css → TaskSettings-CnIBL_Eb.css} +1 -1
- package/dist/ui/assets/{WorkflowsModule-CY9R2zPB.js → WorkflowsModule-BguHeQaV.js} +1 -1
- package/dist/ui/assets/index-MXeWoebC.css +1 -0
- package/dist/ui/assets/index-YJ4k5QNa.js +6 -0
- package/dist/ui/assets/{vendor-icons-QZyhEwgT.js → vendor-icons-C8EED3Oh.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/billingStatusCache.d.ts +21 -0
- package/dist/utils/billingStatusCache.js +70 -0
- package/dist/utils/taskActivity.js +17 -4
- package/dist/utils/taskActivity.test.js +27 -0
- package/dist/utils/taskNormalization.js +10 -2
- package/dist/utils/taskReferences.d.ts +11 -1
- package/dist/utils/taskReferences.js +33 -1
- package/package.json +4 -1
- package/scripts/deploy-prod.sh +41 -0
- package/scripts/deploy-staging.sh +23 -0
- package/scripts/qa-open-lib.mjs +175 -0
- package/scripts/qa-open-lib.mjs.d.ts +49 -0
- package/scripts/qa-open.mjs +352 -0
- package/dist/ui/assets/PlansPage-FcQSNLZd.js +0 -1
- package/dist/ui/assets/TaskSettings-Cw5zYD1L.js +0 -8
- package/dist/ui/assets/index-Bzqy5QGm.js +0 -6
- package/dist/ui/assets/index-DxRiI3tY.css +0 -1
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { chromium } from '@playwright/test';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import {
|
|
8
|
+
QA_CLOUD_BASE_URLS,
|
|
9
|
+
assertQaProjectRootSafe,
|
|
10
|
+
ensureQaRootStructure,
|
|
11
|
+
loadEnvFile,
|
|
12
|
+
matchExpectedWorkspace,
|
|
13
|
+
parseQaArgs,
|
|
14
|
+
resolveExpectedWorkspace,
|
|
15
|
+
resolveQaPaths
|
|
16
|
+
} from './qa-open-lib.mjs';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
21
|
+
const CLI_PATH = path.resolve(REPO_ROOT, 'src/cli.ts');
|
|
22
|
+
const TSX_LOADER_PATH = path.resolve(REPO_ROOT, 'node_modules/tsx/dist/loader.mjs');
|
|
23
|
+
const UI_INDEX_PATH = path.resolve(REPO_ROOT, 'dist/ui/index.html');
|
|
24
|
+
|
|
25
|
+
function fail(message) {
|
|
26
|
+
throw new Error(message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createLogWriter(logFilePath, prefix) {
|
|
30
|
+
const stream = fs.createWriteStream(logFilePath, { flags: 'a' });
|
|
31
|
+
return (chunk) => {
|
|
32
|
+
const text = String(chunk || '');
|
|
33
|
+
process.stdout.write(`${prefix}${text}`);
|
|
34
|
+
stream.write(text);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createErrorLogWriter(logFilePath, prefix) {
|
|
39
|
+
const stream = fs.createWriteStream(logFilePath, { flags: 'a' });
|
|
40
|
+
return (chunk) => {
|
|
41
|
+
const text = String(chunk || '');
|
|
42
|
+
process.stderr.write(`${prefix}${text}`);
|
|
43
|
+
stream.write(text);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function spawnLogged(command, args, options) {
|
|
48
|
+
const child = spawn(command, args, {
|
|
49
|
+
cwd: options.cwd,
|
|
50
|
+
env: options.env,
|
|
51
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
52
|
+
});
|
|
53
|
+
const stdoutWriter = createLogWriter(options.stdoutLogPath, options.stdoutPrefix);
|
|
54
|
+
const stderrWriter = createErrorLogWriter(options.stderrLogPath, options.stderrPrefix);
|
|
55
|
+
child.stdout?.on('data', stdoutWriter);
|
|
56
|
+
child.stderr?.on('data', stderrWriter);
|
|
57
|
+
return child;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isTruthyEnv(raw) {
|
|
61
|
+
return /^(1|true|yes)$/i.test(String(raw || '').trim());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function stripRuntimeOnlyEnv(baseEnv) {
|
|
65
|
+
const env = { ...baseEnv };
|
|
66
|
+
for (const key of Object.keys(env)) {
|
|
67
|
+
if (key.startsWith('TASKFORCE_QA_')) {
|
|
68
|
+
delete env[key];
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (key.startsWith('TASKFORCE_PLAYWRIGHT_')) {
|
|
72
|
+
delete env[key];
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (key === 'TASKFORCE_E2E_EMAIL' || key === 'TASKFORCE_E2E_PASSWORD') {
|
|
76
|
+
delete env[key];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return env;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function waitForChildExit(child, label) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
child.once('exit', (code, signal) => resolve({ label, code, signal }));
|
|
85
|
+
child.once('error', (error) => {
|
|
86
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
87
|
+
reject(new Error(`${label} failed to start: ${message}`));
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function waitForFreshUiBuild(child, startTimeMs, timeoutMs = 120000) {
|
|
93
|
+
const startedAt = Date.now();
|
|
94
|
+
return await new Promise((resolve, reject) => {
|
|
95
|
+
let settled = false;
|
|
96
|
+
const onExit = (code, signal) => {
|
|
97
|
+
if (settled) return;
|
|
98
|
+
settled = true;
|
|
99
|
+
reject(new Error(`UI watch process exited before initial build completed (code=${code ?? 'null'} signal=${signal ?? 'null'}).`));
|
|
100
|
+
};
|
|
101
|
+
child.once('exit', onExit);
|
|
102
|
+
|
|
103
|
+
const interval = setInterval(() => {
|
|
104
|
+
try {
|
|
105
|
+
const stat = fs.statSync(UI_INDEX_PATH);
|
|
106
|
+
if (stat.mtimeMs >= startTimeMs) {
|
|
107
|
+
settled = true;
|
|
108
|
+
clearInterval(interval);
|
|
109
|
+
child.off('exit', onExit);
|
|
110
|
+
resolve();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
// keep polling until timeout
|
|
115
|
+
}
|
|
116
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
117
|
+
settled = true;
|
|
118
|
+
clearInterval(interval);
|
|
119
|
+
child.off('exit', onExit);
|
|
120
|
+
reject(new Error(`Timed out waiting for a fresh standalone UI build at ${UI_INDEX_PATH}.`));
|
|
121
|
+
}
|
|
122
|
+
}, 500);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function runPlaywrightAuth(env) {
|
|
127
|
+
await new Promise((resolve, reject) => {
|
|
128
|
+
const child = spawn('npm', ['run', 'playwright:auth'], {
|
|
129
|
+
cwd: REPO_ROOT,
|
|
130
|
+
env,
|
|
131
|
+
stdio: 'inherit'
|
|
132
|
+
});
|
|
133
|
+
child.once('exit', (code, signal) => {
|
|
134
|
+
if (code === 0) {
|
|
135
|
+
resolve();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
reject(new Error(`Playwright auth bootstrap failed (code=${code ?? 'null'} signal=${signal ?? 'null'}).`));
|
|
139
|
+
});
|
|
140
|
+
child.once('error', reject);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function verifyCloudWorkspace(baseUrl, storageStatePath, expectedWorkspace) {
|
|
145
|
+
const browser = await chromium.launch({ headless: true });
|
|
146
|
+
try {
|
|
147
|
+
const context = await browser.newContext({ storageState: storageStatePath });
|
|
148
|
+
const page = await context.newPage();
|
|
149
|
+
await page.goto(`${baseUrl}/taskforce/`, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
150
|
+
const payload = await page.evaluate(async () => {
|
|
151
|
+
const sessionResponse = await fetch('/api/taskforce/auth/session', {
|
|
152
|
+
method: 'GET',
|
|
153
|
+
credentials: 'include',
|
|
154
|
+
headers: { Accept: 'application/json' }
|
|
155
|
+
});
|
|
156
|
+
const workspacesResponse = await fetch('/api/taskforce/workspaces', {
|
|
157
|
+
method: 'GET',
|
|
158
|
+
credentials: 'include',
|
|
159
|
+
headers: { Accept: 'application/json' }
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
session: await sessionResponse.json(),
|
|
163
|
+
workspaces: await workspacesResponse.json()
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
await context.close();
|
|
167
|
+
const authenticated = Boolean(payload?.session?.authenticated);
|
|
168
|
+
if (!authenticated) {
|
|
169
|
+
fail(`Authentication succeeded earlier, but the saved storage state at ${storageStatePath} is not currently authenticated.`);
|
|
170
|
+
}
|
|
171
|
+
const match = matchExpectedWorkspace(payload?.workspaces, expectedWorkspace);
|
|
172
|
+
if (!match.ok) {
|
|
173
|
+
fail(
|
|
174
|
+
`${match.reason}\n`
|
|
175
|
+
+ 'Check the expected workspace env vars and make sure the account is a member of the intended workspace.'
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
} finally {
|
|
179
|
+
await browser.close();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function buildRuntimeEnv(baseEnv, paths) {
|
|
184
|
+
const isolateHome = isTruthyEnv(baseEnv.TASKFORCE_QA_ISOLATE_HOME);
|
|
185
|
+
const env = stripRuntimeOnlyEnv(baseEnv);
|
|
186
|
+
if (isolateHome) {
|
|
187
|
+
env.HOME = paths.homeDir;
|
|
188
|
+
env.USERPROFILE = paths.homeDir;
|
|
189
|
+
}
|
|
190
|
+
return env;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function main() {
|
|
194
|
+
const { mode, agentId, preferredPort } = parseQaArgs(process.argv.slice(2), process.env);
|
|
195
|
+
if (!mode) {
|
|
196
|
+
fail('Invalid or missing QA mode. Use one of: local, performance, staging.');
|
|
197
|
+
}
|
|
198
|
+
if (!agentId) {
|
|
199
|
+
fail('TASKFORCE_QA_AGENT is required. Example: TASKFORCE_QA_AGENT=codex npm run qa:open -- performance');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const paths = resolveQaPaths({ agentId });
|
|
203
|
+
assertQaProjectRootSafe(paths.projectRoot, REPO_ROOT);
|
|
204
|
+
|
|
205
|
+
const cloudMode = mode !== 'local';
|
|
206
|
+
const envFileExists = fs.existsSync(paths.envFile);
|
|
207
|
+
let envFileVars = {};
|
|
208
|
+
if (envFileExists) {
|
|
209
|
+
envFileVars = loadEnvFile(paths.envFile);
|
|
210
|
+
} else if (cloudMode) {
|
|
211
|
+
fail(
|
|
212
|
+
`Missing QA env file for agent ${agentId}: ${paths.envFile}\n`
|
|
213
|
+
+ 'Create the file with TASKFORCE_E2E_EMAIL, TASKFORCE_E2E_PASSWORD, and the expected workspace id or slug.'
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const projectName = String(envFileVars.TASKFORCE_QA_PROJECT_NAME || `taskforce-qa-${paths.agentId}`).trim();
|
|
218
|
+
ensureQaRootStructure(paths, { projectName });
|
|
219
|
+
|
|
220
|
+
const qaEnv = {
|
|
221
|
+
...process.env,
|
|
222
|
+
...envFileVars,
|
|
223
|
+
TASKFORCE_PLAYWRIGHT_ENV_FILE: paths.envFile,
|
|
224
|
+
TASKFORCE_PLAYWRIGHT_STORAGE_STATE: paths.playwrightStorageState
|
|
225
|
+
};
|
|
226
|
+
const expectedWorkspace = resolveExpectedWorkspace(qaEnv);
|
|
227
|
+
if (cloudMode && !expectedWorkspace) {
|
|
228
|
+
fail(
|
|
229
|
+
`Cloud QA for ${mode} requires TASKFORCE_QA_EXPECTED_WORKSPACE_ID or TASKFORCE_QA_EXPECTED_WORKSPACE_SLUG in ${paths.envFile}.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const runtimeEnv = buildRuntimeEnv(qaEnv, paths);
|
|
233
|
+
|
|
234
|
+
console.log(`[qa:open] Agent: ${paths.agentId}`);
|
|
235
|
+
console.log(`[qa:open] Mode: ${mode}`);
|
|
236
|
+
console.log(`[qa:open] QA root: ${paths.root}`);
|
|
237
|
+
console.log(`[qa:open] External project root: ${paths.projectRoot}`);
|
|
238
|
+
console.log(`[qa:open] Artifact dir: ${paths.artifactsDir}`);
|
|
239
|
+
if (preferredPort) {
|
|
240
|
+
console.log(`[qa:open] Preferred port: ${preferredPort}`);
|
|
241
|
+
}
|
|
242
|
+
if (isTruthyEnv(qaEnv.TASKFORCE_QA_ISOLATE_HOME)) {
|
|
243
|
+
console.log(`[qa:open] HOME isolation enabled: ${paths.homeDir}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (cloudMode) {
|
|
247
|
+
await runPlaywrightAuth({
|
|
248
|
+
...qaEnv,
|
|
249
|
+
TASKFORCE_PLAYWRIGHT_CLOUD_BASE_URL: String(
|
|
250
|
+
qaEnv.TASKFORCE_PLAYWRIGHT_CLOUD_BASE_URL || QA_CLOUD_BASE_URLS[mode]
|
|
251
|
+
)
|
|
252
|
+
});
|
|
253
|
+
await verifyCloudWorkspace(
|
|
254
|
+
String(qaEnv.TASKFORCE_PLAYWRIGHT_CLOUD_BASE_URL || QA_CLOUD_BASE_URLS[mode]),
|
|
255
|
+
paths.playwrightStorageState,
|
|
256
|
+
expectedWorkspace
|
|
257
|
+
);
|
|
258
|
+
console.log('[qa:open] Cloud auth and workspace verification passed.');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const devAppStartedAt = Date.now();
|
|
262
|
+
const devApp = spawnLogged('npm', ['run', 'dev:app'], {
|
|
263
|
+
cwd: REPO_ROOT,
|
|
264
|
+
env: runtimeEnv,
|
|
265
|
+
stdoutLogPath: path.join(paths.logsDir, 'dev-app.stdout.log'),
|
|
266
|
+
stderrLogPath: path.join(paths.logsDir, 'dev-app.stderr.log'),
|
|
267
|
+
stdoutPrefix: '[qa:open:dev-app] ',
|
|
268
|
+
stderrPrefix: '[qa:open:dev-app] '
|
|
269
|
+
});
|
|
270
|
+
let runtime;
|
|
271
|
+
let cleanupSignalHandlers = () => {};
|
|
272
|
+
let shutdownStarted = false;
|
|
273
|
+
let shutdownTimer = null;
|
|
274
|
+
|
|
275
|
+
const shutdown = () => {
|
|
276
|
+
if (shutdownStarted) return;
|
|
277
|
+
shutdownStarted = true;
|
|
278
|
+
for (const child of [runtime, devApp]) {
|
|
279
|
+
if (child?.exitCode == null && child?.signalCode == null) {
|
|
280
|
+
child.kill('SIGTERM');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
shutdownTimer = setTimeout(() => {
|
|
284
|
+
for (const child of [runtime, devApp]) {
|
|
285
|
+
if (child?.exitCode == null && child?.signalCode == null) {
|
|
286
|
+
child.kill('SIGKILL');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}, 5000);
|
|
290
|
+
shutdownTimer.unref?.();
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
await waitForFreshUiBuild(devApp, devAppStartedAt);
|
|
295
|
+
console.log('[qa:open] Standalone UI build is ready.');
|
|
296
|
+
|
|
297
|
+
const runtimeArgs = ['--import', TSX_LOADER_PATH, CLI_PATH, 'open'];
|
|
298
|
+
if (mode !== 'local') runtimeArgs.push(mode);
|
|
299
|
+
if (preferredPort) runtimeArgs.push(String(preferredPort));
|
|
300
|
+
runtime = spawnLogged(process.execPath, runtimeArgs, {
|
|
301
|
+
cwd: paths.projectRoot,
|
|
302
|
+
env: runtimeEnv,
|
|
303
|
+
stdoutLogPath: path.join(paths.logsDir, 'runtime.stdout.log'),
|
|
304
|
+
stderrLogPath: path.join(paths.logsDir, 'runtime.stderr.log'),
|
|
305
|
+
stdoutPrefix: '[qa:open:runtime] ',
|
|
306
|
+
stderrPrefix: '[qa:open:runtime] '
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const devAppExit = waitForChildExit(devApp, 'UI watch process');
|
|
310
|
+
const runtimeExit = waitForChildExit(runtime, 'Runtime process');
|
|
311
|
+
const signalResult = await new Promise((resolve) => {
|
|
312
|
+
const onSignal = (signal) => {
|
|
313
|
+
shutdown();
|
|
314
|
+
resolve({ type: 'signal', signal });
|
|
315
|
+
};
|
|
316
|
+
process.once('SIGINT', onSignal);
|
|
317
|
+
process.once('SIGTERM', onSignal);
|
|
318
|
+
cleanupSignalHandlers = () => {
|
|
319
|
+
process.off('SIGINT', onSignal);
|
|
320
|
+
process.off('SIGTERM', onSignal);
|
|
321
|
+
};
|
|
322
|
+
Promise.race([
|
|
323
|
+
devAppExit.then((event) => ({ type: 'child', event })),
|
|
324
|
+
runtimeExit.then((event) => ({ type: 'child', event }))
|
|
325
|
+
]).then(resolve, (error) => resolve({ type: 'error', error }));
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
cleanupSignalHandlers();
|
|
329
|
+
shutdown();
|
|
330
|
+
await Promise.allSettled([devAppExit, runtimeExit]);
|
|
331
|
+
|
|
332
|
+
if (signalResult.type === 'signal') {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (signalResult.type === 'error') {
|
|
336
|
+
throw signalResult.error;
|
|
337
|
+
}
|
|
338
|
+
const { label, code, signal } = signalResult.event;
|
|
339
|
+
throw new Error(`${label} exited unexpectedly (code=${code ?? 'null'} signal=${signal ?? 'null'}).`);
|
|
340
|
+
} finally {
|
|
341
|
+
cleanupSignalHandlers();
|
|
342
|
+
shutdown();
|
|
343
|
+
if (shutdownTimer) {
|
|
344
|
+
clearTimeout(shutdownTimer);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
main().catch((error) => {
|
|
350
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
351
|
+
process.exitCode = 1;
|
|
352
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as a,j as e}from"./vendor-react-CKJs5o3c.js";import{t as le,y as ue,z as de}from"./index-Bzqy5QGm.js";import{aI as pe,h as me,U as ge}from"./vendor-icons-QZyhEwgT.js";import{u as fe,a as he}from"./vendor-router-BbWMxlnO.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";const ye="_plansWrapper_b8yvs_1",_e="_contentFrame_b8yvs_28",be="_shellOwnsScroll_b8yvs_36",ve="_withChrome_b8yvs_42",Se="_siteVariant_b8yvs_46",Ce="_title_b8yvs_54",ke="_subtitle_b8yvs_63",xe="_header_b8yvs_67",we="_planCard_b8yvs_79",Ie="_grid_b8yvs_92",je="_planName_b8yvs_98",Ne="_featureText_b8yvs_102",Te="_currentBadge_b8yvs_106",Re="_navAction_b8yvs_110",Pe="_backBtn_b8yvs_111",Be="_priceRows_b8yvs_122",$e="_headerBar_b8yvs_139",Ee="_headerActions_b8yvs_148",Ue="_debugMeta_b8yvs_175",Ae="_clickablePlanCard_b8yvs_204",Fe="_activePlanCard_b8yvs_219",Le="_callToAction_b8yvs_272",Me="_primaryCta_b8yvs_284",Ve="_secondaryCta_b8yvs_295",Oe="_currentCta_b8yvs_307",Ye="_featuresList_b8yvs_321",qe="_priceRow_b8yvs_122",ze="_priceRowAmount_b8yvs_346",De="_priceRowInterval_b8yvs_352",Ge="_ctaRow_b8yvs_357",He="_featureItem_b8yvs_363",We="_featureIcon_b8yvs_369",Qe="_emptyState_b8yvs_383",Ke="_marketingEmptyState_b8yvs_392",Je="_emptyBadge_b8yvs_420",Xe="_emptyHighlights_b8yvs_435",Ze="_loader_b8yvs_457",et="_spinner_b8yvs_467",tt="_statusBanner_b8yvs_520",nt="_successBanner_b8yvs_528",rt="_errorBanner_b8yvs_534",n={plansWrapper:ye,contentFrame:_e,shellOwnsScroll:be,withChrome:ve,siteVariant:Se,title:Ce,subtitle:ke,header:xe,planCard:we,grid:Ie,planName:je,featureText:Ne,currentBadge:Te,navAction:Re,backBtn:Pe,priceRows:Be,headerBar:$e,headerActions:Ee,debugMeta:Ue,clickablePlanCard:Ae,activePlanCard:Fe,callToAction:Le,primaryCta:Me,secondaryCta:Ve,currentCta:Oe,featuresList:Ye,priceRow:qe,priceRowAmount:ze,priceRowInterval:De,ctaRow:Ge,featureItem:He,featureIcon:We,emptyState:Qe,marketingEmptyState:Ke,emptyBadge:Je,emptyHighlights:Xe,loader:Ze,spinner:et,statusBanner:tt,successBanner:nt,errorBanner:rt};function st(){const r=globalThis.__DEBUG_MODE__;return typeof r=="boolean"?r:!1}function at(r){const i=String(r?.environment||"").trim(),y=String(r?.runtimeMode||"").trim();return!i&&!y?null:i&&y?`${i} (${y})`:i||y||null}function it(r,i){if(r==null||!i)return"Contact Sales";try{return new Intl.NumberFormat("en-US",{style:"currency",currency:i.toUpperCase(),minimumFractionDigits:0}).format(r/100)}catch{return`${i.toUpperCase()} ${r/100}`}}function ot(){return"$0.00"}function ae(r){return r?r.pricingType==="free"?!0:r.unitAmount!=null&&r.currency!=null:!1}function ie(r){return r?r.pricingType==="free"?ot():it(r.unitAmount,r.currency):"Unavailable"}function oe(r){const i=[r.pricing.month,r.pricing.year].filter(Boolean);if(i.some(m=>m.pricingType==="free"))return 0;const y=i.map(m=>m.pricingType==="stripe"&&typeof m.unitAmount=="number"?m.unitAmount:null).filter(m=>m!=null);return y.length>0?Math.min(...y):Number.POSITIVE_INFINITY}function ct(r){const i=String(r||"").trim().toLowerCase();return i==="active"||i==="trialing"||i==="grace"}function lt({heading:r,subtitle:i,variant:y="app",chrome:m,footer:P,isAuthenticated:B,authSessionResolved:z=!0,currentPlanId:V,currentPlanVersionId:S,currentEntitlementState:J,canManageCurrentPlan:d=!0,pricingEndpoint:f,statusBanner:_,onBack:D,backLabel:G,topActions:$,shellOwnsScroll:H=!1,showPageBackButton:c=!0,fallbackPricingSource:U=null,onSelectPlan:v}){const[w,E]=a.useState([]),[I,W]=a.useState(null),[A,O]=a.useState(!0),[F,C]=a.useState(null),[Q,X]=a.useState(0);a.useEffect(()=>{let s=!0;return(async()=>{const h=String(f||"/api/taskforce/public/pricing").trim()||"/api/taskforce/public/pricing";s&&(O(!0),C(null));try{const l=await fetch(h,{method:"GET",credentials:"include"}),u=await l.json().catch(()=>({}));if(l.ok){s&&(E(Array.isArray(u?.plans)?u.plans:[]),W(u?.pricingSource&&typeof u.pricingSource=="object"?u.pricingSource:null));return}throw new Error(String(u?.error||`Failed to load pricing (${l.status})`))}catch(l){if(s){const u=l instanceof Error?l.message:"Failed to load pricing";C(u||"Failed to load pricing"),W(null)}}finally{s&&O(!1)}})(),()=>{s=!1}},[f,Q]);const k=a.useMemo(()=>[...w].sort((s,g)=>{const h=oe(s),l=oe(g);if(h!==l)return h-l;const u=s.defaultVersion?.versionNumber||0,p=g.defaultVersion?.versionNumber||0;return u!==p?u-p:String(s.displayName||s.planId).localeCompare(String(g.displayName||g.planId))}),[w]),Y=a.useMemo(()=>at(I||U),[U,I]),q=`${n.plansWrapper} ${H?n.shellOwnsScroll:"tf-scrollbar"} ${y==="site"?n.siteVariant:""} ${m?n.withChrome:""}`.trim(),j=s=>H?e.jsxs(e.Fragment,{children:[m,s,P]}):e.jsxs("div",{className:le.standaloneWrapper,"data-theme":"dark",children:[m,s,P]});return j(A||!z?e.jsx(e.Fragment,{children:e.jsx("div",{className:q,children:e.jsx("div",{className:n.contentFrame,children:e.jsxs("div",{className:n.loader,children:[e.jsx(me,{size:48,className:n.spinner}),e.jsx("p",{children:"Loading plans..."})]})})})}):F?e.jsx(e.Fragment,{children:e.jsx("div",{className:q,children:e.jsx("div",{className:n.contentFrame,children:e.jsxs("div",{className:n.emptyState,children:[e.jsx("h2",{children:"Connecting to plan pricing..."}),e.jsx("p",{children:F}),e.jsx("button",{className:n.backBtn,onClick:()=>X(s=>s+1),children:"Retry"})]})})})}):e.jsx(e.Fragment,{children:e.jsx("div",{className:q,children:e.jsxs("div",{className:n.contentFrame,children:[e.jsxs("div",{className:n.header,children:[!m&&(c||$)&&e.jsxs("div",{className:n.headerBar,children:[e.jsx("div",{children:c?e.jsx("button",{className:n.backBtn,onClick:D,children:G}):null}),e.jsx("div",{className:n.headerActions,children:$})]}),e.jsx("h1",{className:n.title,children:r}),i&&e.jsx("p",{className:n.subtitle,children:i}),st()&&Y?e.jsxs("p",{className:n.debugMeta,children:["Pricing source: ",Y]}):null,_&&e.jsx("div",{className:`${n.statusBanner} ${_.type==="success"?n.successBanner:n.errorBanner}`,children:_.message})]}),k.length===0?e.jsxs("div",{className:`${n.emptyState} ${n.marketingEmptyState}`,children:[e.jsx("span",{className:n.emptyBadge,children:"Coming Soon"}),e.jsx("h2",{children:"Paid plans are getting their final polish."}),e.jsx("p",{children:"Taskforce pricing is on the way with flexible options for solo operators, teams, and larger rollouts. Check back soon for launch tiers, feature bundles, and early access details."}),e.jsxs("div",{className:n.emptyHighlights,"aria-label":"Upcoming pricing highlights",children:[e.jsx("span",{children:"Launch-ready tiers"}),e.jsx("span",{children:"Team billing controls"}),e.jsx("span",{children:"Feature-based packaging"})]})]}):e.jsx("div",{className:n.grid,children:k.map(s=>{const g=s.pricing.month,h=s.pricing.year,l=ae(g),u=ae(h),p=String(s.defaultVersion?.planVersionId||"").trim(),x=!!(p&&S&&S===p&&ct(J))||!S&&!!(V&&V===s.planId),N=l?"month":u?"year":null,L=N==="year"?h:g,T=!!p&&(x?d:!!N),K=()=>{if(p){if(x){if(!d)return;v({planId:s.planId,planVersionId:p,interval:N||"month",pricingType:L?.pricingType||"stripe",isCurrentPlan:!0});return}N&&v({planId:s.planId,planVersionId:p,interval:N,pricingType:L?.pricingType||"stripe",isCurrentPlan:!1})}};return e.jsxs("div",{className:`${n.planCard} ${x?n.activePlanCard:""} ${T?n.clickablePlanCard:""}`,"data-testid":`plan-card-${s.planId}`,role:T?"button":void 0,tabIndex:T?0:void 0,onClick:T?K:void 0,onKeyDown:T?t=>{(t.key==="Enter"||t.key===" ")&&(t.preventDefault(),K())}:void 0,children:[x&&e.jsx("span",{className:n.currentBadge,children:"Current Plan"}),e.jsx("h3",{className:n.planName,children:s.displayName}),e.jsx("div",{className:n.featuresList,children:Array.isArray(s.features)&&s.features.length>0?s.features.map((t,o)=>e.jsxs("div",{className:n.featureItem,children:[e.jsx(pe,{className:n.featureIcon}),e.jsx("span",{className:n.featureText,children:String(t.featureKey||"").replace(/[_\-.]+/g," ").replace(/\b\w/g,b=>b.toUpperCase())})]},`${t.featureKey}-${o}`)):e.jsx("span",{className:n.featureText,children:"No additional features included"})}),e.jsxs("div",{className:n.priceRows,children:[e.jsxs("div",{className:n.priceRow,children:[e.jsx("span",{className:n.priceRowAmount,children:ie(g)}),e.jsx("span",{className:n.priceRowInterval,children:"/ month"})]}),e.jsxs("div",{className:n.priceRow,children:[e.jsx("span",{className:n.priceRowAmount,children:ie(h)}),e.jsx("span",{className:n.priceRowInterval,children:"/ year"})]})]}),x?e.jsx("button",{className:`${n.callToAction} ${n.currentCta}`,"data-testid":`plan-manage-${s.planId}`,onClick:t=>{t.stopPropagation(),d&&v({planId:s.planId,planVersionId:p,interval:N||"month",pricingType:L?.pricingType||"stripe",isCurrentPlan:!0})},disabled:!p||!d,children:B&&d?"Manage Plan":"Current Plan"}):e.jsxs("div",{className:n.ctaRow,children:[e.jsx("button",{className:`${n.callToAction} ${n.secondaryCta}`,"data-testid":`plan-select-${s.planId}-month`,onClick:t=>{t.stopPropagation(),v({planId:s.planId,planVersionId:p,interval:"month",pricingType:g?.pricingType||"stripe",isCurrentPlan:!1})},disabled:!p||!l,children:g?.pricingType==="free"?B?"Select Free":"Get Started Free":B?"Choose Monthly":"Get Started Monthly"}),e.jsx("button",{className:`${n.callToAction} ${n.primaryCta}`,"data-testid":`plan-select-${s.planId}-year`,onClick:t=>{t.stopPropagation(),v({planId:s.planId,planVersionId:p,interval:"year",pricingType:h?.pricingType||"stripe",isCurrentPlan:!1})},disabled:!p||!u,children:h?.pricingType==="free"?B?"Select Free":"Get Started Free":B?"Choose Yearly":"Get Started Yearly"})]})]},s.planId)})})]})})}))}const ut=1600,dt=new Set(["active","trialing","grace"]),M={planSelectionRequired:"Choose a plan to continue.",misconfigured:"Signup monetization is misconfigured. Contact support or a system administrator.",missingEntitlement:"No entitlement is linked to this account."};function pt(r){return r==="year"?"year":"month"}function ce(r){const i=String(r||"").trim();return i?i.replace(/\/+$/,""):""}function te(r={}){const i=new URLSearchParams;i.set("screen","plans");for(const[y,m]of Object.entries(r)){const P=String(m||"").trim();P&&i.set(y,P)}return`/?${i.toString()}`}function bt({isAuthenticated:r,runtimeMode:i,authSessionResolved:y,currentTheme:m="dark",projectName:P,currentWorkspaceId:B,apiBaseUrl:z,connectedEnvironmentSource:V,embedded:S=!1,shellOwnsScroll:J=!1}){const d=fe(),f=he(),[_,D]=a.useState(null),[G,$]=a.useState(!1),[H,c]=a.useState(null),U=a.useRef(!1),v=a.useRef(null),w=a.useMemo(()=>new URLSearchParams(f.search),[f.search]),E=String(w.get("checkout")||"").trim().toLowerCase(),I=String(w.get("gate")||"").trim().toLowerCase(),W=String(w.get("planId")||"").trim(),A=String(w.get("planVersionId")||"").trim(),O=pt(w.get("interval")),F=String(w.get("session_id")||"").trim(),C=a.useMemo(()=>ce(z),[z]),Q=a.useMemo(()=>ce(V),[V]),X=a.useMemo(()=>({environment:ue(Q||C),runtimeMode:i==="cloud"?"cloud":"local"}),[C,Q,i]),k=a.useCallback(t=>{const o=String(t||"").trim();return C?`${C}${o.startsWith("/")?o:`/${o}`}`:o},[C]),Y=a.useCallback(()=>{const t=new URLSearchParams(f.search);t.delete("screen"),t.delete("gate"),t.delete("checkout"),t.delete("planId"),t.delete("planVersionId"),t.delete("interval");const o=t.toString();d(`/${o?`?${o}`:""}${f.hash||""}`)},[f.hash,f.search,d]),q=a.useMemo(()=>[C?`${C}/api/taskforce/public/pricing`:"","/api/taskforce/public/pricing"].map(o=>String(o||"").trim()).filter((o,b,R)=>o&&R.indexOf(o)===b),[C]),j=a.useMemo(()=>{const t=String(_?.entitlementState||"").trim().toLowerCase();return dt.has(t)},[_?.entitlementState]),s=a.useMemo(()=>j?null:I==="plan_selection_required"?M.planSelectionRequired:I==="misconfigured"?M.misconfigured:I==="missing_entitlement"?M.missingEntitlement:null,[I,j]),g=a.useCallback(()=>{const t=new URLSearchParams(f.search);t.delete("checkout"),t.delete("session_id"),t.delete("gate"),t.delete("planId"),t.delete("planVersionId"),t.delete("interval");const o=t.toString();d(`${f.pathname}${o?`?${o}`:""}${f.hash||""}`,{replace:!0})},[f.hash,f.pathname,f.search,d]),h=a.useCallback(()=>{v.current!=null&&window.clearTimeout(v.current),v.current=window.setTimeout(()=>{g(),v.current=null},ut)},[g]),l=a.useCallback(async()=>{if(!r){D(null);return}try{const t=await fetch(k("/api/taskforce/billing/status"),{method:"GET",credentials:"include"});if(!t.ok)return;const o=await t.json().catch(()=>({}));D(o||null)}catch{}},[k,r]),u=a.useCallback(async(t,o)=>{if(!t){c({type:"error",message:"Selected plan is missing a default plan version."});return}$(!0),c(null);try{const b=await fetch(k("/api/taskforce/billing/checkout-session"),{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({planVersionId:t,interval:o})}),R=await b.json().catch(()=>({}));if(!b.ok){c({type:"error",message:String(R?.error||"Unable to start checkout for selected plan.")});return}const Z=String(R?.checkoutState||"").trim().toLowerCase();if(Z==="free"){c({type:"success",message:"Plan selected. Your access is ready."}),await l();return}if(Z==="updated"){c({type:"success",message:String(R?.message||"Your subscription was updated successfully.")}),await l();return}if(Z==="already_current_plan"){c({type:"success",message:String(R?.message||"You are already on this plan.")}),await l();return}const ne=String(R?.returnBaseUrl||"").trim();if(i==="local"&&ne)try{const ee=window.location.origin,se=new URL(ne,ee).origin;if(se!==ee){c({type:"error",message:`Checkout return is misconfigured for this runtime. Expected ${ee} but got ${se}.`});return}}catch{c({type:"error",message:"Checkout return URL is invalid for this runtime."});return}const re=String(R?.url||"").trim();if(!re){c({type:"error",message:"Checkout did not return a redirect URL."});return}window.location.assign(re)}catch{c({type:"error",message:"Unable to start checkout for selected plan."})}finally{$(!1)}},[k,l]),p=a.useCallback(async t=>{if(!t)return!1;try{const o=await fetch(k("/api/taskforce/billing/checkout-session/confirm"),{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:t})}),b=await o.json().catch(()=>({}));return o.ok?(await l(),!0):(c({type:"error",message:String(b?.error||"Unable to confirm checkout completion.")}),!1)}catch{return c({type:"error",message:"Unable to confirm checkout completion."}),!1}},[k,l]),x=a.useCallback(async()=>{if(!r){d(`/login?mode=login&next=${encodeURIComponent(te())}`);return}$(!0),c(null);try{const t=await fetch(k("/api/taskforce/billing/portal-session"),{method:"POST",credentials:"include"}),o=await t.json().catch(()=>({}));if(!t.ok){c({type:"error",message:String(o?.error||"Failed to open billing portal.")});return}const b=String(o?.url||"").trim();if(!b){c({type:"error",message:"Portal session did not return a redirect URL."});return}window.location.assign(b)}catch{c({type:"error",message:"Failed to open billing portal."})}finally{$(!1)}},[k,r,d]);a.useEffect(()=>{l()},[l]),a.useEffect(()=>{j&&I==="missing_entitlement"&&g()},[g,I,j]),a.useEffect(()=>()=>{v.current!=null&&window.clearTimeout(v.current)},[]),a.useEffect(()=>{if(E==="success"){U.current=!0,c({type:"success",message:"Checkout completed successfully. Finalizing your plan..."}),(async()=>(F&&await p(F)||await l(),c({type:"success",message:"Checkout completed successfully."}),h()))();return}if(E==="cancel"){c({type:"error",message:"Checkout was canceled. No charge was made."}),h();return}if((E==="start"||E==="free")&&r&&A&&!U.current){U.current=!0,u(A,O);return}if(s){c({type:"error",message:s});return}c(t=>!t||t.type!=="error"?t:t.message===M.planSelectionRequired||t.message===M.misconfigured||t.message===M.missingEntitlement?null:t)},[E,p,s,r,l,O,A,F,h,u]);const N=a.useCallback(async t=>{if(!r){const o=t.pricingType==="free"?"free":"start",b=te({checkout:o,planId:t.planId,planVersionId:t.planVersionId,interval:t.interval});d(`/login?mode=register&planId=${encodeURIComponent(t.planId)}&planVersionId=${encodeURIComponent(t.planVersionId)}&interval=${encodeURIComponent(t.interval)}&next=${encodeURIComponent(b)}`);return}if(t.isCurrentPlan){if(!_?.stripeCustomerId){c({type:"success",message:"Your current plan does not use Stripe billing."});return}await x();return}if(t.pricingType==="free"){await u(t.planVersionId,t.interval);return}await u(t.planVersionId,t.interval)},[r,d,x,u]),L=a.useCallback(()=>{if(S){Y();return}if(window.history.length>1){d(-1);return}d("/")},[Y,S,d]),T=S?e.jsx(e.Fragment,{children:r&&_?.stripeCustomerId&&e.jsx("button",{className:n.navAction,onClick:()=>{x()},disabled:G,children:"Manage billing"})}):e.jsxs(e.Fragment,{children:[e.jsx("button",{className:n.navAction,onClick:L,children:"Back"}),r&&_?.stripeCustomerId&&e.jsx("button",{className:n.navAction,onClick:()=>{x()},disabled:G,children:"Manage billing"}),!r&&e.jsxs("button",{className:n.navAction,onClick:()=>d(`/login?mode=login&next=${encodeURIComponent(te())}`),children:[e.jsx(ge,{size:16,style:{marginRight:"8px",verticalAlign:"text-bottom"}}),"Sign In"]})]}),K=S?null:e.jsx(de,{projectName:P,currentWorkspaceId:B,runtimeMode:i==="cloud"?"cloud":"local",theme:m,actions:T});return e.jsx(lt,{chrome:K,heading:"Subscription Plans",subtitle:"Manage your subscription and explore available features. Your current plan is highlighted below.",isAuthenticated:r,authSessionResolved:y,currentPlanId:String(_?.planId||W||"").trim()||null,currentPlanVersionId:String(_?.planVersionId||A||"").trim()||null,currentEntitlementState:String(_?.entitlementState||"").trim()||null,canManageCurrentPlan:!!_?.stripeCustomerId,pricingEndpoint:q[0],statusBanner:H,onBack:L,backLabel:S?"Back to Board":"Back to App",topActions:S?T:null,shellOwnsScroll:J,showPageBackButton:!S,fallbackPricingSource:X,onSelectPlan:N})}export{bt as PlansPage};
|