@straiffi/archon 1.2.0 → 1.2.2

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.
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Archon</title>
8
- <script type="module" crossorigin src="/assets/index-WH13gBCE.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-BGi8klde.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-BYbx6iT9.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/badge-Bpry9xkS.js">
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-Bw66dtZG.css">
@@ -1225,7 +1225,11 @@ const getBundleRecentCommitSubjects = (project, bundle) => {
1225
1225
  }
1226
1226
  };
1227
1227
  const serializeBundleDetails = (project, bundle, extras = {}) => {
1228
- const tickets = bundle ? listDetailedBundleTickets(project.id, bundle.id) : [];
1228
+ const serializedBundle = serializeBundleRow(project.id, bundle);
1229
+ if (!serializedBundle) {
1230
+ throw new Error(`Failed to serialize bundle ${bundle.id}`);
1231
+ }
1232
+ const tickets = listDetailedBundleTickets(project.id, bundle.id);
1229
1233
  const latestSessionTicket = resolveBundleLatestSessionTicket(tickets);
1230
1234
  const resolvedTargetTicket = extras.target_ticket_id
1231
1235
  ? tickets.find(ticket => ticket.id === extras.target_ticket_id) ?? null
@@ -1235,7 +1239,7 @@ const serializeBundleDetails = (project, bundle, extras = {}) => {
1235
1239
  });
1236
1240
  const bundleReviewState = getBundleReviewState(project.id, tickets);
1237
1241
  return {
1238
- bundle: serializeBundleRow(project.id, bundle),
1242
+ bundle: serializedBundle,
1239
1243
  tickets,
1240
1244
  execution_order_ticket_ids: tickets.map(ticket => ticket.id),
1241
1245
  latest_session_ticket_id: latestSessionTicket?.id ?? null,
@@ -1267,6 +1271,64 @@ const serializeBundleConversationDetails = (project, bundle, extras = {}) => {
1267
1271
  target_ticket_id: resolvedTargetTicket?.id ?? null,
1268
1272
  };
1269
1273
  };
1274
+ const getProjectBundleDetails = (projectId, bundleId) => {
1275
+ const project = getProjectById(projectId);
1276
+ if (!project) {
1277
+ return null;
1278
+ }
1279
+ ensureProjectRootBundle(project.id);
1280
+ const bundle = getBundle(bundleId, project.id);
1281
+ if (!bundle) {
1282
+ return null;
1283
+ }
1284
+ return serializeBundleDetails(project, bundle);
1285
+ };
1286
+ const submitBundleFollowUpResponse = (projectId, bundleId, payload, broadcaster) => {
1287
+ const project = getProjectById(projectId);
1288
+ if (!project) {
1289
+ return { ok: false, status: 404, error: 'Project not found' };
1290
+ }
1291
+ const bundle = getBundle(bundleId, project.id);
1292
+ if (!bundle) {
1293
+ return { ok: false, status: 404, error: 'Bundle not found' };
1294
+ }
1295
+ const instruction = typeof payload?.instruction === 'string'
1296
+ ? payload.instruction.trim()
1297
+ : '';
1298
+ if (!instruction) {
1299
+ return { ok: false, status: 400, error: 'instruction is required' };
1300
+ }
1301
+ const tickets = listDetailedBundleTickets(project.id, bundle.id).filter(ticket => ticket.state !== 'plan');
1302
+ const targetTicket = resolveBundleTargetTicket(tickets);
1303
+ if (!targetTicket) {
1304
+ return { ok: false, status: 409, error: 'No non-plan tickets are available for follow-up in this bundle' };
1305
+ }
1306
+ const effectiveProject = getEffectiveProject(targetTicket);
1307
+ if (!effectiveProject) {
1308
+ return { ok: false, status: 400, error: 'Ticket project no longer exists' };
1309
+ }
1310
+ const branch = resolveTicketBranch(targetTicket);
1311
+ if (!branch || targetTicket.has_worktree === false || !isWorktreeReady(branch, effectiveProject)) {
1312
+ return { ok: false, status: 400, error: 'Worktree not ready yet' };
1313
+ }
1314
+ const started = startFollowUp(targetTicket, broadcaster, instruction);
1315
+ if (!started) {
1316
+ return { ok: false, status: 409, error: 'Ticket is already running' };
1317
+ }
1318
+ createTicketMessage({
1319
+ ticketId: targetTicket.id,
1320
+ role: 'user',
1321
+ kind: 'follow_up',
1322
+ content: instruction,
1323
+ });
1324
+ return {
1325
+ ok: true,
1326
+ status: 200,
1327
+ value: serializeBundleDetails(project, bundle, {
1328
+ target_ticket_id: targetTicket.id,
1329
+ }),
1330
+ };
1331
+ };
1270
1332
  const resolveBundleReviewOwner = (projectId, bundleId) => {
1271
1333
  const tickets = listDetailedBundleTickets(projectId, bundleId);
1272
1334
  const candidate = tickets.find(ticket => {
@@ -1869,6 +1931,9 @@ app.post('/mobile-access/enable', async (req, res) => {
1869
1931
  if (!requireDesktopMobileAccessRequest(req, res)) {
1870
1932
  return;
1871
1933
  }
1934
+ const preferredProjectId = typeof req.body?.preferred_project_id === 'string'
1935
+ ? req.body.preferred_project_id
1936
+ : null;
1872
1937
  const requestedMode = req.body?.mode === 'custom_public_url'
1873
1938
  ? 'custom_public_url'
1874
1939
  : req.body?.mode === 'ngrok'
@@ -1884,7 +1949,10 @@ app.post('/mobile-access/enable', async (req, res) => {
1884
1949
  mode: requestedMode,
1885
1950
  clientBuild: resolveClientBuildPaths(import.meta.url),
1886
1951
  broadcaster: io,
1952
+ preferredProjectId,
1887
1953
  reviewBundleTickets,
1954
+ getBundleDetails: getProjectBundleDetails,
1955
+ submitBundleFollowUp: submitBundleFollowUpResponse,
1888
1956
  publicBaseUrl,
1889
1957
  }));
1890
1958
  });
@@ -1900,7 +1968,10 @@ app.post('/mobile-access/regenerate-pairing', (req, res) => {
1900
1968
  return;
1901
1969
  }
1902
1970
  res.setHeader('Cache-Control', 'no-store');
1903
- res.json(regenerateMobilePairingChallenge());
1971
+ const preferredProjectId = typeof req.body?.preferred_project_id === 'string'
1972
+ ? req.body.preferred_project_id
1973
+ : null;
1974
+ res.json(regenerateMobilePairingChallenge(preferredProjectId));
1904
1975
  });
1905
1976
  app.post('/mobile-access/sessions/:sessionId/revoke', (req, res) => {
1906
1977
  if (!requireDesktopMobileAccessRequest(req, res)) {
@@ -2839,42 +2910,11 @@ app.post('/bundles/:id/follow-up', (req, res) => {
2839
2910
  if ('error' in result) {
2840
2911
  return res.status(400).json({ error: result.error });
2841
2912
  }
2842
- const bundle = getBundle(req.params.id, result.project.id);
2843
- if (!bundle) {
2844
- return res.status(404).json({ error: 'Bundle not found' });
2845
- }
2846
- const instruction = typeof req.body?.instruction === 'string'
2847
- ? req.body.instruction.trim()
2848
- : '';
2849
- if (!instruction) {
2850
- return res.status(400).json({ error: 'instruction is required' });
2851
- }
2852
- const tickets = listDetailedBundleTickets(result.project.id, bundle.id).filter(ticket => ticket.state !== 'plan');
2853
- const targetTicket = resolveBundleTargetTicket(tickets);
2854
- if (!targetTicket) {
2855
- return res.status(409).json({ error: 'No non-plan tickets are available for follow-up in this bundle' });
2913
+ const response = submitBundleFollowUpResponse(result.project.id, req.params.id, req.body, io);
2914
+ if (!response.ok) {
2915
+ return res.status(response.status).json({ error: response.error });
2856
2916
  }
2857
- const project = getEffectiveProject(targetTicket);
2858
- if (!project) {
2859
- return res.status(400).json({ error: 'Ticket project no longer exists' });
2860
- }
2861
- const branch = resolveTicketBranch(targetTicket);
2862
- if (!branch || targetTicket.has_worktree === false || !isWorktreeReady(branch, project)) {
2863
- return res.status(400).json({ error: 'Worktree not ready yet' });
2864
- }
2865
- const started = startFollowUp(targetTicket, io, instruction);
2866
- if (!started) {
2867
- return res.status(409).json({ error: 'Ticket is already running' });
2868
- }
2869
- createTicketMessage({
2870
- ticketId: targetTicket.id,
2871
- role: 'user',
2872
- kind: 'follow_up',
2873
- content: instruction,
2874
- });
2875
- return res.json(serializeBundleDetails(result.project, bundle, {
2876
- target_ticket_id: targetTicket.id,
2877
- }));
2917
+ return res.status(response.status).json(response.value);
2878
2918
  });
2879
2919
  app.get('/tool', (_req, res) => {
2880
2920
  res.json({ tool: config.tool ?? 'opencode' });