@straiffi/archon 1.2.1 → 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-BgmAtYCf.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 => {
@@ -1889,6 +1951,8 @@ app.post('/mobile-access/enable', async (req, res) => {
1889
1951
  broadcaster: io,
1890
1952
  preferredProjectId,
1891
1953
  reviewBundleTickets,
1954
+ getBundleDetails: getProjectBundleDetails,
1955
+ submitBundleFollowUp: submitBundleFollowUpResponse,
1892
1956
  publicBaseUrl,
1893
1957
  }));
1894
1958
  });
@@ -2846,42 +2910,11 @@ app.post('/bundles/:id/follow-up', (req, res) => {
2846
2910
  if ('error' in result) {
2847
2911
  return res.status(400).json({ error: result.error });
2848
2912
  }
2849
- const bundle = getBundle(req.params.id, result.project.id);
2850
- if (!bundle) {
2851
- return res.status(404).json({ error: 'Bundle not found' });
2852
- }
2853
- const instruction = typeof req.body?.instruction === 'string'
2854
- ? req.body.instruction.trim()
2855
- : '';
2856
- if (!instruction) {
2857
- return res.status(400).json({ error: 'instruction is required' });
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 });
2858
2916
  }
2859
- const tickets = listDetailedBundleTickets(result.project.id, bundle.id).filter(ticket => ticket.state !== 'plan');
2860
- const targetTicket = resolveBundleTargetTicket(tickets);
2861
- if (!targetTicket) {
2862
- return res.status(409).json({ error: 'No non-plan tickets are available for follow-up in this bundle' });
2863
- }
2864
- const project = getEffectiveProject(targetTicket);
2865
- if (!project) {
2866
- return res.status(400).json({ error: 'Ticket project no longer exists' });
2867
- }
2868
- const branch = resolveTicketBranch(targetTicket);
2869
- if (!branch || targetTicket.has_worktree === false || !isWorktreeReady(branch, project)) {
2870
- return res.status(400).json({ error: 'Worktree not ready yet' });
2871
- }
2872
- const started = startFollowUp(targetTicket, io, instruction);
2873
- if (!started) {
2874
- return res.status(409).json({ error: 'Ticket is already running' });
2875
- }
2876
- createTicketMessage({
2877
- ticketId: targetTicket.id,
2878
- role: 'user',
2879
- kind: 'follow_up',
2880
- content: instruction,
2881
- });
2882
- return res.json(serializeBundleDetails(result.project, bundle, {
2883
- target_ticket_id: targetTicket.id,
2884
- }));
2917
+ return res.status(response.status).json(response.value);
2885
2918
  });
2886
2919
  app.get('/tool', (_req, res) => {
2887
2920
  res.json({ tool: config.tool ?? 'opencode' });