@ctrl-spc/cs 0.6.0 → 0.7.1

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.
@@ -0,0 +1,68 @@
1
+ async function read(query, subject) {
2
+ const { data, error } = await query;
3
+ if (error)
4
+ throw new Error(`could not read ${subject}: ${error.message}`);
5
+ return data ?? [];
6
+ }
7
+ /**
8
+ * A workflow, its stages in order, and the exits off them.
9
+ *
10
+ * SORTED HERE RATHER THAN TRUSTED, which is the web reader's own rule: order is
11
+ * the whole point of a workflow, positions are deliberately not unique (a
12
+ * reorder rewrites the set), and a tie has to break the same way twice.
13
+ *
14
+ * FOUR READS RATHER THAN ONE EMBED. `cliv2_workflow_stages_in_workflow` carries
15
+ * two foreign keys into `cliv2_workflow_stages` (`stage_id` and
16
+ * `loop_to_stage_id`), so a nested embed of the stage document would need
17
+ * disambiguating by constraint name — a coupling to a constraint's spelling for
18
+ * the sake of saving a round trip on a tool call an agent makes once.
19
+ */
20
+ export async function readWorkflow(client, workflowId) {
21
+ const found = await read(client.from('cliv2_workflows').select('id, name, description, ending').eq('id', workflowId), `workflow ${workflowId}`);
22
+ if (found.length === 0) {
23
+ throw new Error(`could not read workflow ${workflowId}: there is no such row, or it is not yours`);
24
+ }
25
+ const row = found[0];
26
+ const [links, exits] = await Promise.all([
27
+ read(client.from('cliv2_workflow_stages_in_workflow').select('stage_id, position')
28
+ .eq('workflow_id', workflowId), `the stages of workflow ${workflowId}`),
29
+ read(client.from('cliv2_workflow_stage_exits').select('stage_id, to_stage_id, condition, position')
30
+ .eq('workflow_id', workflowId), `the exits of workflow ${workflowId}`),
31
+ ]);
32
+ const ordered = [...links].sort((a, b) => a.position - b.position || a.stage_id.localeCompare(b.stage_id));
33
+ const stageRows = ordered.length === 0 ? [] : await read(client.from('cliv2_workflow_stages').select('id, name, description, body')
34
+ .in('id', ordered.map((link) => link.stage_id)), `the stage documents of workflow ${workflowId}`);
35
+ const byId = new Map(stageRows.map((stage) => [stage.id, stage]));
36
+ return {
37
+ id: row.id,
38
+ name: row.name,
39
+ description: row.description ?? '',
40
+ /* Defaulted here as well as in the column, for the web reader's reason: a
41
+ workflow written before the ending existed reads back as 'end'. */
42
+ ending: row.ending ?? 'end',
43
+ stages: ordered.map((link) => {
44
+ const stage = byId.get(link.stage_id);
45
+ /* A LISTED STAGE THAT DID NOT COME BACK IS AN ERROR, NEVER A GAP. The
46
+ join cascades on delete and archiving is not deletion, so this cannot
47
+ happen through the product — and a stage silently missing from the
48
+ middle of a recipe would be a different process presented as this one. */
49
+ if (!stage) {
50
+ throw new Error(`could not read the stage documents of workflow ${workflowId}: stage ${link.stage_id} `
51
+ + 'is in the workflow but its document did not come back');
52
+ }
53
+ return {
54
+ id: stage.id,
55
+ name: stage.name,
56
+ description: stage.description ?? '',
57
+ body: stage.body ?? '',
58
+ };
59
+ }),
60
+ exits: [...exits]
61
+ .sort((a, b) => a.position - b.position || a.to_stage_id.localeCompare(b.to_stage_id))
62
+ .map((exit) => ({
63
+ stageId: exit.stage_id,
64
+ toStageId: exit.to_stage_id,
65
+ condition: exit.condition,
66
+ })),
67
+ };
68
+ }
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@ctrl-spc/cs",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "CTRL+SPC — minimal, reliable per-machine agent presence. Sign-in, auto-start, agent detection, heartbeat presence, and ping acknowledgement.",
5
5
  "engines": {
6
6
  "node": ">=22"
7
7
  },
8
8
  "type": "module",
9
9
  "bin": {
10
- "cs": "dist/index.js"
10
+ "cs": "dist/index.js",
11
+ "cs3": "dist/panel3/cli.js"
11
12
  },
12
13
  "files": [
13
14
  "dist"