spidersan 0.20.1 → 0.20.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.
- package/CHANGELOG.md +8 -0
- package/dist/bin/command-registry.d.ts +25 -0
- package/dist/bin/command-registry.d.ts.map +1 -0
- package/dist/bin/command-registry.js +29 -0
- package/dist/bin/command-registry.js.map +1 -0
- package/dist/bin/spidersan.d.ts +16 -0
- package/dist/bin/spidersan.d.ts.map +1 -0
- package/dist/bin/spidersan.js +98 -0
- package/dist/bin/spidersan.js.map +1 -0
- package/dist/commands/abandon.d.ts +8 -0
- package/dist/commands/abandon.d.ts.map +1 -0
- package/dist/commands/abandon.js +43 -0
- package/dist/commands/abandon.js.map +1 -0
- package/dist/commands/cleanup.d.ts +8 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +50 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/config.d.ts +15 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +309 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/conflicts.d.ts +34 -0
- package/dist/commands/conflicts.d.ts.map +1 -0
- package/dist/commands/conflicts.js +773 -0
- package/dist/commands/conflicts.js.map +1 -0
- package/dist/commands/context.d.ts +8 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +104 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/cross-conflicts.d.ts +17 -0
- package/dist/commands/cross-conflicts.d.ts.map +1 -0
- package/dist/commands/cross-conflicts.js +273 -0
- package/dist/commands/cross-conflicts.js.map +1 -0
- package/dist/commands/depends.d.ts +11 -0
- package/dist/commands/depends.d.ts.map +1 -0
- package/dist/commands/depends.js +63 -0
- package/dist/commands/depends.js.map +1 -0
- package/dist/commands/doctor.d.ts +30 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +558 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/git-watch.d.ts +24 -0
- package/dist/commands/git-watch.d.ts.map +1 -0
- package/dist/commands/git-watch.js +84 -0
- package/dist/commands/git-watch.js.map +1 -0
- package/dist/commands/github-sync.d.ts +42 -0
- package/dist/commands/github-sync.d.ts.map +1 -0
- package/dist/commands/github-sync.js +191 -0
- package/dist/commands/github-sync.js.map +1 -0
- package/dist/commands/index.d.ts +25 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +29 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +23 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +45 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/log.d.ts +7 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +110 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/merge-order.d.ts +9 -0
- package/dist/commands/merge-order.d.ts.map +1 -0
- package/dist/commands/merge-order.js +100 -0
- package/dist/commands/merge-order.js.map +1 -0
- package/dist/commands/merged.d.ts +8 -0
- package/dist/commands/merged.d.ts.map +1 -0
- package/dist/commands/merged.js +37 -0
- package/dist/commands/merged.js.map +1 -0
- package/dist/commands/pr-check.d.ts +24 -0
- package/dist/commands/pr-check.d.ts.map +1 -0
- package/dist/commands/pr-check.js +85 -0
- package/dist/commands/pr-check.js.map +1 -0
- package/dist/commands/ready-check.d.ts +11 -0
- package/dist/commands/ready-check.d.ts.map +1 -0
- package/dist/commands/ready-check.js +150 -0
- package/dist/commands/ready-check.js.map +1 -0
- package/dist/commands/rebase-helper.d.ts +3 -0
- package/dist/commands/rebase-helper.d.ts.map +1 -0
- package/dist/commands/rebase-helper.js +52 -0
- package/dist/commands/rebase-helper.js.map +1 -0
- package/dist/commands/register.d.ts +10 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +176 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/registry-sync.d.ts +9 -0
- package/dist/commands/registry-sync.d.ts.map +1 -0
- package/dist/commands/registry-sync.js +129 -0
- package/dist/commands/registry-sync.js.map +1 -0
- package/dist/commands/stale.d.ts +17 -0
- package/dist/commands/stale.d.ts.map +1 -0
- package/dist/commands/stale.js +136 -0
- package/dist/commands/stale.js.map +1 -0
- package/dist/commands/sync.d.ts +21 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +78 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/verify-trunk.d.ts +18 -0
- package/dist/commands/verify-trunk.d.ts.map +1 -0
- package/dist/commands/verify-trunk.js +77 -0
- package/dist/commands/verify-trunk.js.map +1 -0
- package/dist/commands/watch.d.ts +8 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +509 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/activity.d.ts +53 -0
- package/dist/lib/activity.d.ts.map +1 -0
- package/dist/lib/activity.js +271 -0
- package/dist/lib/activity.js.map +1 -0
- package/dist/lib/agent-errors.d.ts +41 -0
- package/dist/lib/agent-errors.d.ts.map +1 -0
- package/dist/lib/agent-errors.js +145 -0
- package/dist/lib/agent-errors.js.map +1 -0
- package/dist/lib/ai/context-builder.d.ts +16 -0
- package/dist/lib/ai/context-builder.d.ts.map +1 -0
- package/dist/lib/ai/context-builder.js +229 -0
- package/dist/lib/ai/context-builder.js.map +1 -0
- package/dist/lib/ai/event-handler.d.ts +21 -0
- package/dist/lib/ai/event-handler.d.ts.map +1 -0
- package/dist/lib/ai/event-handler.js +111 -0
- package/dist/lib/ai/event-handler.js.map +1 -0
- package/dist/lib/ai/index.d.ts +13 -0
- package/dist/lib/ai/index.d.ts.map +1 -0
- package/dist/lib/ai/index.js +11 -0
- package/dist/lib/ai/index.js.map +1 -0
- package/dist/lib/ai/llm-client.d.ts +37 -0
- package/dist/lib/ai/llm-client.d.ts.map +1 -0
- package/dist/lib/ai/llm-client.js +225 -0
- package/dist/lib/ai/llm-client.js.map +1 -0
- package/dist/lib/ai/reasoner.d.ts +11 -0
- package/dist/lib/ai/reasoner.d.ts.map +1 -0
- package/dist/lib/ai/reasoner.js +227 -0
- package/dist/lib/ai/reasoner.js.map +1 -0
- package/dist/lib/ai/setup.d.ts +40 -0
- package/dist/lib/ai/setup.d.ts.map +1 -0
- package/dist/lib/ai/setup.js +154 -0
- package/dist/lib/ai/setup.js.map +1 -0
- package/dist/lib/ai/types.d.ts +135 -0
- package/dist/lib/ai/types.d.ts.map +1 -0
- package/dist/lib/ai/types.js +42 -0
- package/dist/lib/ai/types.js.map +1 -0
- package/dist/lib/ast.d.ts +44 -0
- package/dist/lib/ast.d.ts.map +1 -0
- package/dist/lib/ast.js +160 -0
- package/dist/lib/ast.js.map +1 -0
- package/dist/lib/carries.d.ts +22 -0
- package/dist/lib/carries.d.ts.map +1 -0
- package/dist/lib/carries.js +56 -0
- package/dist/lib/carries.js.map +1 -0
- package/dist/lib/colony-subscriber.d.ts +56 -0
- package/dist/lib/colony-subscriber.d.ts.map +1 -0
- package/dist/lib/colony-subscriber.js +265 -0
- package/dist/lib/colony-subscriber.js.map +1 -0
- package/dist/lib/config.d.ts +62 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +235 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/conflict-analyzer.d.ts +33 -0
- package/dist/lib/conflict-analyzer.d.ts.map +1 -0
- package/dist/lib/conflict-analyzer.js +114 -0
- package/dist/lib/conflict-analyzer.js.map +1 -0
- package/dist/lib/conflict-renderer.d.ts +7 -0
- package/dist/lib/conflict-renderer.d.ts.map +1 -0
- package/dist/lib/conflict-renderer.js +145 -0
- package/dist/lib/conflict-renderer.js.map +1 -0
- package/dist/lib/conflict-tier.d.ts +20 -0
- package/dist/lib/conflict-tier.d.ts.map +1 -0
- package/dist/lib/conflict-tier.js +49 -0
- package/dist/lib/conflict-tier.js.map +1 -0
- package/dist/lib/crypto.d.ts +71 -0
- package/dist/lib/crypto.d.ts.map +1 -0
- package/dist/lib/crypto.js +176 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/ecosystem-env.d.ts +33 -0
- package/dist/lib/ecosystem-env.d.ts.map +1 -0
- package/dist/lib/ecosystem-env.js +62 -0
- package/dist/lib/ecosystem-env.js.map +1 -0
- package/dist/lib/errors.d.ts +35 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +71 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/git-events-subscriber.d.ts +86 -0
- package/dist/lib/git-events-subscriber.d.ts.map +1 -0
- package/dist/lib/git-events-subscriber.js +870 -0
- package/dist/lib/git-events-subscriber.js.map +1 -0
- package/dist/lib/git-merge-analyzer.d.ts +38 -0
- package/dist/lib/git-merge-analyzer.d.ts.map +1 -0
- package/dist/lib/git-merge-analyzer.js +23 -0
- package/dist/lib/git-merge-analyzer.js.map +1 -0
- package/dist/lib/git.d.ts +103 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +670 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/github.d.ts +151 -0
- package/dist/lib/github.d.ts.map +1 -0
- package/dist/lib/github.js +425 -0
- package/dist/lib/github.js.map +1 -0
- package/dist/lib/graph.d.ts +38 -0
- package/dist/lib/graph.d.ts.map +1 -0
- package/dist/lib/graph.js +165 -0
- package/dist/lib/graph.js.map +1 -0
- package/dist/lib/machine.d.ts +14 -0
- package/dist/lib/machine.d.ts.map +1 -0
- package/dist/lib/machine.js +38 -0
- package/dist/lib/machine.js.map +1 -0
- package/dist/lib/notify.d.ts +68 -0
- package/dist/lib/notify.d.ts.map +1 -0
- package/dist/lib/notify.js +111 -0
- package/dist/lib/notify.js.map +1 -0
- package/dist/lib/outline.d.ts +6 -0
- package/dist/lib/outline.d.ts.map +1 -0
- package/dist/lib/outline.js +60 -0
- package/dist/lib/outline.js.map +1 -0
- package/dist/lib/push-registry.d.ts +22 -0
- package/dist/lib/push-registry.d.ts.map +1 -0
- package/dist/lib/push-registry.js +21 -0
- package/dist/lib/push-registry.js.map +1 -0
- package/dist/lib/reconcile.d.ts +100 -0
- package/dist/lib/reconcile.d.ts.map +1 -0
- package/dist/lib/reconcile.js +121 -0
- package/dist/lib/reconcile.js.map +1 -0
- package/dist/lib/regex-utils.d.ts +6 -0
- package/dist/lib/regex-utils.d.ts.map +1 -0
- package/dist/lib/regex-utils.js +24 -0
- package/dist/lib/regex-utils.js.map +1 -0
- package/dist/lib/register-renderer.d.ts +10 -0
- package/dist/lib/register-renderer.d.ts.map +1 -0
- package/dist/lib/register-renderer.js +19 -0
- package/dist/lib/register-renderer.js.map +1 -0
- package/dist/lib/remote-drift.d.ts +61 -0
- package/dist/lib/remote-drift.d.ts.map +1 -0
- package/dist/lib/remote-drift.js +227 -0
- package/dist/lib/remote-drift.js.map +1 -0
- package/dist/lib/security.d.ts +72 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +180 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/semantic-analysis.d.ts +31 -0
- package/dist/lib/semantic-analysis.d.ts.map +1 -0
- package/dist/lib/semantic-analysis.js +120 -0
- package/dist/lib/semantic-analysis.js.map +1 -0
- package/dist/lib/supabase-credentials.d.ts +36 -0
- package/dist/lib/supabase-credentials.d.ts.map +1 -0
- package/dist/lib/supabase-credentials.js +43 -0
- package/dist/lib/supabase-credentials.js.map +1 -0
- package/dist/lib/trunk.d.ts +39 -0
- package/dist/lib/trunk.d.ts.map +1 -0
- package/dist/lib/trunk.js +116 -0
- package/dist/lib/trunk.js.map +1 -0
- package/dist/lib/update-check.d.ts +11 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +113 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/watch-renderer.d.ts +13 -0
- package/dist/lib/watch-renderer.d.ts.map +1 -0
- package/dist/lib/watch-renderer.js +35 -0
- package/dist/lib/watch-renderer.js.map +1 -0
- package/dist/storage/adapter.d.ts +81 -0
- package/dist/storage/adapter.d.ts.map +1 -0
- package/dist/storage/adapter.js +8 -0
- package/dist/storage/adapter.js.map +1 -0
- package/dist/storage/branch-registry-store.d.ts +13 -0
- package/dist/storage/branch-registry-store.d.ts.map +1 -0
- package/dist/storage/branch-registry-store.js +2 -0
- package/dist/storage/branch-registry-store.js.map +1 -0
- package/dist/storage/factory.d.ts +6 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +14 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/index.d.ts +10 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/json-branch-registry-store.d.ts +19 -0
- package/dist/storage/json-branch-registry-store.d.ts.map +1 -0
- package/dist/storage/json-branch-registry-store.js +138 -0
- package/dist/storage/json-branch-registry-store.js.map +1 -0
- package/dist/storage/local.d.ts +20 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +40 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/memory-branch-registry-store.d.ts +16 -0
- package/dist/storage/memory-branch-registry-store.d.ts.map +1 -0
- package/dist/storage/memory-branch-registry-store.js +68 -0
- package/dist/storage/memory-branch-registry-store.js.map +1 -0
- package/dist/storage/supabase-registry-sync-client-impl.d.ts +40 -0
- package/dist/storage/supabase-registry-sync-client-impl.d.ts.map +1 -0
- package/dist/storage/supabase-registry-sync-client-impl.js +278 -0
- package/dist/storage/supabase-registry-sync-client-impl.js.map +1 -0
- package/dist/storage/supabase-registry-sync-client.d.ts +9 -0
- package/dist/storage/supabase-registry-sync-client.d.ts.map +1 -0
- package/dist/storage/supabase-registry-sync-client.js +2 -0
- package/dist/storage/supabase-registry-sync-client.js.map +1 -0
- package/dist/storage/supabase.d.ts +28 -0
- package/dist/storage/supabase.d.ts.map +1 -0
- package/dist/storage/supabase.js +56 -0
- package/dist/storage/supabase.js.map +1 -0
- package/dist/types/cloud.d.ts +102 -0
- package/dist/types/cloud.d.ts.map +1 -0
- package/dist/types/cloud.js +13 -0
- package/dist/types/cloud.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type NodeId = string;
|
|
2
|
+
/**
|
|
3
|
+
* Build a conflict adjacency map: for each branch, which other branches
|
|
4
|
+
* share overlapping files?
|
|
5
|
+
* Pure function — takes branch data, returns a Map.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildConflictGraph(branches: Array<{
|
|
8
|
+
name: string;
|
|
9
|
+
files: string[];
|
|
10
|
+
}>): Map<NodeId, NodeId[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Fold declared dependencies into a conflict graph as DIRECTED edges.
|
|
13
|
+
* An edge u→v gives v in-degree, so v sorts after u: "A depends on B"
|
|
14
|
+
* becomes edge B→A (B merges first). Because conflict edges are symmetric,
|
|
15
|
+
* a dependent that ALSO file-overlaps its dep (the normal stacked-branch
|
|
16
|
+
* case) forms a 2-cycle that would swallow the dep edge and fall back to
|
|
17
|
+
* alphabetical cycle-breaking — so the opposing dependent→dep edge is
|
|
18
|
+
* REMOVED: the declared direction wins over the undirected conflict.
|
|
19
|
+
* Unknown/self deps are dropped by topologicalSort's normalization.
|
|
20
|
+
* Returns a new map; inputs untouched.
|
|
21
|
+
*/
|
|
22
|
+
export declare function addDependencyEdges(graph: Map<NodeId, NodeId[]>, branches: Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
dependsOn?: string[];
|
|
25
|
+
}>): Map<NodeId, NodeId[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Topological sort with tie-breaking by blocking count.
|
|
28
|
+
* Returns branches in recommended merge order.
|
|
29
|
+
* Cycles are broken by inserting branches at earliest valid position.
|
|
30
|
+
* blockingCounts is optional; when omitted, sort is alphabetical within tied groups.
|
|
31
|
+
*/
|
|
32
|
+
export declare function topologicalSort(nodes: NodeId[], graph: Map<NodeId, NodeId[]>, blockingCounts?: Map<NodeId, number>): NodeId[];
|
|
33
|
+
/**
|
|
34
|
+
* For each node, count how many other nodes are (transitively) blocked by it.
|
|
35
|
+
* Used for rarest-first prioritization.
|
|
36
|
+
*/
|
|
37
|
+
export declare function calculateBlockingCounts(nodes: NodeId[], graph: Map<NodeId, NodeId[]>): Map<NodeId, number>;
|
|
38
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/lib/graph.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AA0C5B;;;;GAIG;AACH,wBAAgB,kBAAkB,CAC9B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GACnD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAqCvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC5B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GACxD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAsBvB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC5B,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,MAAM,EAAE,CAoDV;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC7B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA2BrB"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
function compareNodeIds(left, right, blockingCounts) {
|
|
2
|
+
if (blockingCounts) {
|
|
3
|
+
const leftCount = blockingCounts.get(left) ?? 0;
|
|
4
|
+
const rightCount = blockingCounts.get(right) ?? 0;
|
|
5
|
+
if (leftCount !== rightCount) {
|
|
6
|
+
return rightCount - leftCount;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
return left.localeCompare(right);
|
|
10
|
+
}
|
|
11
|
+
function normalizeGraph(nodes, graph) {
|
|
12
|
+
const knownNodes = new Set(nodes);
|
|
13
|
+
const normalized = new Map();
|
|
14
|
+
for (const node of nodes) {
|
|
15
|
+
const neighbors = graph.get(node) ?? [];
|
|
16
|
+
const uniqueNeighbors = new Set();
|
|
17
|
+
for (const neighbor of neighbors) {
|
|
18
|
+
if (neighbor !== node && knownNodes.has(neighbor)) {
|
|
19
|
+
uniqueNeighbors.add(neighbor);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
normalized.set(node, Array.from(uniqueNeighbors).sort((left, right) => left.localeCompare(right)));
|
|
23
|
+
}
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build a conflict adjacency map: for each branch, which other branches
|
|
28
|
+
* share overlapping files?
|
|
29
|
+
* Pure function — takes branch data, returns a Map.
|
|
30
|
+
*/
|
|
31
|
+
export function buildConflictGraph(branches) {
|
|
32
|
+
const fileToBranches = new Map();
|
|
33
|
+
for (const branch of branches) {
|
|
34
|
+
for (const file of new Set(branch.files)) {
|
|
35
|
+
let branchNames = fileToBranches.get(file);
|
|
36
|
+
if (!branchNames) {
|
|
37
|
+
branchNames = new Set();
|
|
38
|
+
fileToBranches.set(file, branchNames);
|
|
39
|
+
}
|
|
40
|
+
branchNames.add(branch.name);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const graph = new Map();
|
|
44
|
+
for (const branch of branches) {
|
|
45
|
+
const conflicts = new Set();
|
|
46
|
+
for (const file of new Set(branch.files)) {
|
|
47
|
+
const branchNames = fileToBranches.get(file);
|
|
48
|
+
if (!branchNames) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (const otherBranch of branchNames) {
|
|
52
|
+
if (otherBranch !== branch.name) {
|
|
53
|
+
conflicts.add(otherBranch);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
graph.set(branch.name, Array.from(conflicts).sort((left, right) => left.localeCompare(right)));
|
|
58
|
+
}
|
|
59
|
+
return graph;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Fold declared dependencies into a conflict graph as DIRECTED edges.
|
|
63
|
+
* An edge u→v gives v in-degree, so v sorts after u: "A depends on B"
|
|
64
|
+
* becomes edge B→A (B merges first). Because conflict edges are symmetric,
|
|
65
|
+
* a dependent that ALSO file-overlaps its dep (the normal stacked-branch
|
|
66
|
+
* case) forms a 2-cycle that would swallow the dep edge and fall back to
|
|
67
|
+
* alphabetical cycle-breaking — so the opposing dependent→dep edge is
|
|
68
|
+
* REMOVED: the declared direction wins over the undirected conflict.
|
|
69
|
+
* Unknown/self deps are dropped by topologicalSort's normalization.
|
|
70
|
+
* Returns a new map; inputs untouched.
|
|
71
|
+
*/
|
|
72
|
+
export function addDependencyEdges(graph, branches) {
|
|
73
|
+
const merged = new Map();
|
|
74
|
+
for (const [node, neighbors] of graph.entries()) {
|
|
75
|
+
merged.set(node, [...neighbors]);
|
|
76
|
+
}
|
|
77
|
+
for (const branch of branches) {
|
|
78
|
+
for (const dep of branch.dependsOn ?? []) {
|
|
79
|
+
const out = merged.get(dep) ?? [];
|
|
80
|
+
if (!out.includes(branch.name)) {
|
|
81
|
+
out.push(branch.name);
|
|
82
|
+
}
|
|
83
|
+
merged.set(dep, out);
|
|
84
|
+
const back = merged.get(branch.name);
|
|
85
|
+
if (back) {
|
|
86
|
+
merged.set(branch.name, back.filter((n) => n !== dep));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return merged;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Topological sort with tie-breaking by blocking count.
|
|
94
|
+
* Returns branches in recommended merge order.
|
|
95
|
+
* Cycles are broken by inserting branches at earliest valid position.
|
|
96
|
+
* blockingCounts is optional; when omitted, sort is alphabetical within tied groups.
|
|
97
|
+
*/
|
|
98
|
+
export function topologicalSort(nodes, graph, blockingCounts) {
|
|
99
|
+
const normalizedGraph = normalizeGraph(nodes, graph);
|
|
100
|
+
const inDegree = new Map();
|
|
101
|
+
const remaining = new Set(nodes);
|
|
102
|
+
const available = [];
|
|
103
|
+
const order = [];
|
|
104
|
+
for (const node of nodes) {
|
|
105
|
+
inDegree.set(node, 0);
|
|
106
|
+
}
|
|
107
|
+
for (const neighbors of normalizedGraph.values()) {
|
|
108
|
+
for (const neighbor of neighbors) {
|
|
109
|
+
inDegree.set(neighbor, (inDegree.get(neighbor) ?? 0) + 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const node of nodes) {
|
|
113
|
+
if ((inDegree.get(node) ?? 0) === 0) {
|
|
114
|
+
available.push(node);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
available.sort((left, right) => compareNodeIds(left, right, blockingCounts));
|
|
118
|
+
while (remaining.size > 0) {
|
|
119
|
+
let current = available.shift();
|
|
120
|
+
if (!current) {
|
|
121
|
+
current = Array.from(remaining).sort((left, right) => compareNodeIds(left, right, blockingCounts))[0];
|
|
122
|
+
}
|
|
123
|
+
if (!current || !remaining.has(current)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
remaining.delete(current);
|
|
127
|
+
order.push(current);
|
|
128
|
+
for (const neighbor of normalizedGraph.get(current) ?? []) {
|
|
129
|
+
const nextDegree = (inDegree.get(neighbor) ?? 0) - 1;
|
|
130
|
+
inDegree.set(neighbor, nextDegree);
|
|
131
|
+
if (nextDegree === 0 && remaining.has(neighbor)) {
|
|
132
|
+
available.push(neighbor);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
available.sort((left, right) => compareNodeIds(left, right, blockingCounts));
|
|
136
|
+
}
|
|
137
|
+
return order;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* For each node, count how many other nodes are (transitively) blocked by it.
|
|
141
|
+
* Used for rarest-first prioritization.
|
|
142
|
+
*/
|
|
143
|
+
export function calculateBlockingCounts(nodes, graph) {
|
|
144
|
+
const normalizedGraph = normalizeGraph(nodes, graph);
|
|
145
|
+
const counts = new Map();
|
|
146
|
+
for (const node of nodes) {
|
|
147
|
+
const seen = new Set();
|
|
148
|
+
const stack = [...(normalizedGraph.get(node) ?? [])];
|
|
149
|
+
while (stack.length > 0) {
|
|
150
|
+
const current = stack.pop();
|
|
151
|
+
if (!current || current === node || seen.has(current)) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
seen.add(current);
|
|
155
|
+
for (const neighbor of normalizedGraph.get(current) ?? []) {
|
|
156
|
+
if (!seen.has(neighbor) && neighbor !== node) {
|
|
157
|
+
stack.push(neighbor);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
counts.set(node, seen.size);
|
|
162
|
+
}
|
|
163
|
+
return counts;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/lib/graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for this machine's identity (~/.envoak/machine.json).
|
|
3
|
+
*
|
|
4
|
+
* Four near-copies of this loader had drifted apart (registry-sync, dashboard,
|
|
5
|
+
* github-sync, and an inline one in cross-conflicts): two read `id || machine_id`,
|
|
6
|
+
* one read `machine_id || id`, and the missing-file fallback was either
|
|
7
|
+
* `fallback-<hostname>` or the literal 'unknown'. The id is used to exclude THIS
|
|
8
|
+
* machine when pulling cross-machine registries, so a divergent fallback means a
|
|
9
|
+
* machine can fail to filter itself out and conflict against its own branches
|
|
10
|
+
* (tb-ly0b).
|
|
11
|
+
*/
|
|
12
|
+
import type { MachineIdentity } from '../types/cloud.js';
|
|
13
|
+
export declare function loadMachineIdentity(): Promise<MachineIdentity>;
|
|
14
|
+
//# sourceMappingURL=machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../src/lib/machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,eAAe,CAAC,CAsBpE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for this machine's identity (~/.envoak/machine.json).
|
|
3
|
+
*
|
|
4
|
+
* Four near-copies of this loader had drifted apart (registry-sync, dashboard,
|
|
5
|
+
* github-sync, and an inline one in cross-conflicts): two read `id || machine_id`,
|
|
6
|
+
* one read `machine_id || id`, and the missing-file fallback was either
|
|
7
|
+
* `fallback-<hostname>` or the literal 'unknown'. The id is used to exclude THIS
|
|
8
|
+
* machine when pulling cross-machine registries, so a divergent fallback means a
|
|
9
|
+
* machine can fail to filter itself out and conflict against its own branches
|
|
10
|
+
* (tb-ly0b).
|
|
11
|
+
*/
|
|
12
|
+
import { readFile } from 'fs/promises';
|
|
13
|
+
import { existsSync } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
import { homedir, hostname as osHostname } from 'os';
|
|
16
|
+
export async function loadMachineIdentity() {
|
|
17
|
+
const configPath = join(homedir(), '.envoak', 'machine.json');
|
|
18
|
+
if (existsSync(configPath)) {
|
|
19
|
+
try {
|
|
20
|
+
const data = JSON.parse(await readFile(configPath, 'utf-8'));
|
|
21
|
+
const id = data.id || data.machine_id;
|
|
22
|
+
if (id) {
|
|
23
|
+
return {
|
|
24
|
+
id,
|
|
25
|
+
name: data.name || data.machine_name || 'unknown',
|
|
26
|
+
hostname: data.hostname || 'unknown',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Unreadable/corrupt machine.json — fall through to the hostname
|
|
32
|
+
// fallback rather than throwing; identity is advisory, not critical.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const host = osHostname();
|
|
36
|
+
return { id: `fallback-${host}`, name: host, hostname: host };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine.js","sourceRoot":"","sources":["../../src/lib/machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,MAAM,IAAI,CAAC;AAGrD,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAE9D,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YAC7D,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC;YACtC,IAAI,EAAE,EAAE,CAAC;gBACL,OAAO;oBACH,EAAE;oBACF,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,IAAI,SAAS;oBACjD,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;iBACvC,CAAC;YACN,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,iEAAiE;YACjE,qEAAqE;QACzE,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,OAAO,EAAE,EAAE,EAAE,YAAY,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* notify — the delivery seam for git-watch alerts.
|
|
3
|
+
*
|
|
4
|
+
* Every alert in git-events-subscriber.ts used to inline its own transport:
|
|
5
|
+
* `execFile('envoak', ['hive','signal',...], cb)` with the callback discarding
|
|
6
|
+
* the result. When `envoak hive signal` was removed (Envoak#148, ADR-0002) all
|
|
7
|
+
* five sites died at once, and because no site inspected its own result the
|
|
8
|
+
* daemon went on logging success for a month (sp-8iqm).
|
|
9
|
+
*
|
|
10
|
+
* So the transport lives behind one interface, and that interface returns a
|
|
11
|
+
* RESULT rather than swallowing it. A caller cannot accidentally claim success:
|
|
12
|
+
* there is a value it has to look at.
|
|
13
|
+
*
|
|
14
|
+
* Adapters are selected by env, so swapping transports is config, not code:
|
|
15
|
+
* SPIDERSAN_ROOM_TOKEN — post to a toak.me room (POST /api/chat/send).
|
|
16
|
+
* Name matches the vault secret toak/SPIDERSAN_ROOM_TOKEN,
|
|
17
|
+
* so `envoak vault inject` supplies it directly.
|
|
18
|
+
* unset — nullNotifier: reports undelivered, never pretends
|
|
19
|
+
*/
|
|
20
|
+
export type Delivered = {
|
|
21
|
+
ok: true;
|
|
22
|
+
} | {
|
|
23
|
+
ok: false;
|
|
24
|
+
reason: string;
|
|
25
|
+
};
|
|
26
|
+
export interface NotifyEvent {
|
|
27
|
+
repo: string;
|
|
28
|
+
branch: string;
|
|
29
|
+
/** Conflict tier. Callers gate on this themselves — policy is not ours. */
|
|
30
|
+
tier: number;
|
|
31
|
+
/** Human-readable line. May be LLM-generated — never key off this. */
|
|
32
|
+
message: string;
|
|
33
|
+
/**
|
|
34
|
+
* Stable identity of the underlying event, for deduplication. MUST be
|
|
35
|
+
* built only from deterministic facts (repo, branch, sha, action) — the
|
|
36
|
+
* tier>=2 path escalates to an LLM, so `message` differs between two
|
|
37
|
+
* deliveries of the same event and cannot be used here.
|
|
38
|
+
*/
|
|
39
|
+
dedupe: string;
|
|
40
|
+
/** GitHub login or agent that triggered it, when known. */
|
|
41
|
+
agent?: string;
|
|
42
|
+
files?: string[];
|
|
43
|
+
/** Extra fields passed through to the room as structured metadata. */
|
|
44
|
+
details?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A transport. One function — nothing here needs a class.
|
|
48
|
+
*
|
|
49
|
+
* CONTRACT: a Notifier MUST NOT throw. It reports failure by returning
|
|
50
|
+
* `{ok:false, reason}`. Call sites sit inside `catch` blocks that swallow
|
|
51
|
+
* AI-layer errors, so a thrown transport error would be misread as an AI
|
|
52
|
+
* failure and vanish — the same silence this module exists to remove.
|
|
53
|
+
*/
|
|
54
|
+
export type Notifier = (event: NotifyEvent) => Promise<Delivered>;
|
|
55
|
+
/** Post to a toak.me room. The room join token is a read+write secret. */
|
|
56
|
+
export declare function toakRoomNotifier(token: string, fetchImpl?: typeof fetch): Notifier;
|
|
57
|
+
/**
|
|
58
|
+
* No transport configured. Reports undelivered rather than pretending — the
|
|
59
|
+
* whole point of sp-8iqm is that a severed channel must be visible.
|
|
60
|
+
*/
|
|
61
|
+
export declare const nullNotifier: Notifier;
|
|
62
|
+
export declare function createNotifier(): Notifier;
|
|
63
|
+
/**
|
|
64
|
+
* Deliver, then log the real outcome. This is the ONLY place allowed to say a
|
|
65
|
+
* notification was sent, and it says so only after looking at the result.
|
|
66
|
+
*/
|
|
67
|
+
export declare function deliver(notifier: Notifier, event: NotifyEvent, log: (m: string) => void): Promise<Delivered>;
|
|
68
|
+
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAmClE,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,QAAQ,CAiCzF;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,QAGzB,CAAC;AAEH,wBAAgB,cAAc,IAAI,QAAQ,CAGzC;AAED;;;GAGG;AACH,wBAAsB,OAAO,CACzB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACzB,OAAO,CAAC,SAAS,CAAC,CAKpB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* notify — the delivery seam for git-watch alerts.
|
|
3
|
+
*
|
|
4
|
+
* Every alert in git-events-subscriber.ts used to inline its own transport:
|
|
5
|
+
* `execFile('envoak', ['hive','signal',...], cb)` with the callback discarding
|
|
6
|
+
* the result. When `envoak hive signal` was removed (Envoak#148, ADR-0002) all
|
|
7
|
+
* five sites died at once, and because no site inspected its own result the
|
|
8
|
+
* daemon went on logging success for a month (sp-8iqm).
|
|
9
|
+
*
|
|
10
|
+
* So the transport lives behind one interface, and that interface returns a
|
|
11
|
+
* RESULT rather than swallowing it. A caller cannot accidentally claim success:
|
|
12
|
+
* there is a value it has to look at.
|
|
13
|
+
*
|
|
14
|
+
* Adapters are selected by env, so swapping transports is config, not code:
|
|
15
|
+
* SPIDERSAN_ROOM_TOKEN — post to a toak.me room (POST /api/chat/send).
|
|
16
|
+
* Name matches the vault secret toak/SPIDERSAN_ROOM_TOKEN,
|
|
17
|
+
* so `envoak vault inject` supplies it directly.
|
|
18
|
+
* unset — nullNotifier: reports undelivered, never pretends
|
|
19
|
+
*/
|
|
20
|
+
import { createHash } from 'crypto';
|
|
21
|
+
import { hostname } from 'os';
|
|
22
|
+
const TOAK_URL = process.env.TOAK_CHAT_URL || 'https://toak.me/api/chat/send';
|
|
23
|
+
// Bare name, no `agent:` prefix. Room allowlists match the sender string
|
|
24
|
+
// EXACTLY — the `agent:<id>` form in toak's send route governs verification
|
|
25
|
+
// upgrade, not allowlist matching, and conflating the two means an approved
|
|
26
|
+
// `spidersan` would not admit a sender of `agent:spidersan`. The machine goes
|
|
27
|
+
// in metadata instead, so one approval covers every host.
|
|
28
|
+
const SENDER = process.env.SPIDERSAN_AGENT || 'spidersan';
|
|
29
|
+
const MACHINE = process.env.TREEBIRD_MACHINE || hostname();
|
|
30
|
+
/**
|
|
31
|
+
* Stable per event, so a catch-up poll that replays an event minutes later
|
|
32
|
+
* does not post it twice. The server's own default is a ~5s time bucket, far
|
|
33
|
+
* shorter than our reconnect window.
|
|
34
|
+
*/
|
|
35
|
+
function idempotencyKey(e) {
|
|
36
|
+
return createHash('sha256')
|
|
37
|
+
.update([e.repo, e.branch, e.tier, e.dedupe].join(' '))
|
|
38
|
+
.digest('hex');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The original emits were fire-and-forget with `{ timeout: 10000 }`. These are
|
|
42
|
+
* awaited inside the event handlers, so an unbounded fetch would stall the
|
|
43
|
+
* handler and queue every event behind it. Same budget as before.
|
|
44
|
+
*/
|
|
45
|
+
const TIMEOUT_MS = 10_000;
|
|
46
|
+
function format(e) {
|
|
47
|
+
const who = e.agent ? ` by ${e.agent}` : '';
|
|
48
|
+
const files = e.files?.length ? `\nfiles: ${e.files.slice(0, 10).join(', ')}` : '';
|
|
49
|
+
return `TIER ${e.tier} conflict - ${e.repo}/${e.branch}${who}\n${e.message}${files}`;
|
|
50
|
+
}
|
|
51
|
+
/** Post to a toak.me room. The room join token is a read+write secret. */
|
|
52
|
+
export function toakRoomNotifier(token, fetchImpl = fetch) {
|
|
53
|
+
return async (event) => {
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetchImpl(TOAK_URL, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
token,
|
|
61
|
+
sender: SENDER,
|
|
62
|
+
content: format(event),
|
|
63
|
+
idempotency_key: idempotencyKey(event),
|
|
64
|
+
metadata: {
|
|
65
|
+
machine: MACHINE,
|
|
66
|
+
repo: event.repo,
|
|
67
|
+
branch: event.branch,
|
|
68
|
+
tier: event.tier,
|
|
69
|
+
agent: event.agent,
|
|
70
|
+
files: event.files,
|
|
71
|
+
...event.details,
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
// The POST response IS the call-time liveness check — no probe needed.
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const body = await res.text().catch(() => '');
|
|
78
|
+
return { ok: false, reason: `${res.status} ${body.slice(0, 120)}`.trim() };
|
|
79
|
+
}
|
|
80
|
+
return { ok: true };
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* No transport configured. Reports undelivered rather than pretending — the
|
|
89
|
+
* whole point of sp-8iqm is that a severed channel must be visible.
|
|
90
|
+
*/
|
|
91
|
+
export const nullNotifier = async () => ({
|
|
92
|
+
ok: false,
|
|
93
|
+
reason: 'no transport configured (set SPIDERSAN_ROOM_TOKEN)',
|
|
94
|
+
});
|
|
95
|
+
export function createNotifier() {
|
|
96
|
+
const token = process.env.SPIDERSAN_ROOM_TOKEN;
|
|
97
|
+
return token ? toakRoomNotifier(token) : nullNotifier;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Deliver, then log the real outcome. This is the ONLY place allowed to say a
|
|
101
|
+
* notification was sent, and it says so only after looking at the result.
|
|
102
|
+
*/
|
|
103
|
+
export async function deliver(notifier, event, log) {
|
|
104
|
+
const result = await notifier(event);
|
|
105
|
+
if (result.ok)
|
|
106
|
+
log(`notified: TIER ${event.tier} ${event.repo}/${event.branch}`);
|
|
107
|
+
else
|
|
108
|
+
log(`NOT NOTIFIED (TIER ${event.tier} ${event.repo}/${event.branch}): ${result.reason}`);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type SymbolConflict, type SymbolInfo } from './ast.js';
|
|
2
|
+
/** ext includes the dot, e.g. '.py'. Only function/class/method symbols are kept. */
|
|
3
|
+
export declare function outlineSymbols(content: string, ext: string): SymbolInfo[] | null;
|
|
4
|
+
/** Same contract as ASTParser.findSymbolConflicts, for any language ast-grep outlines. null = unavailable. */
|
|
5
|
+
export declare function findOutlineConflicts(ext: string, contentA: string, labelA: string, contentB: string, labelB: string, baseContent?: string): SymbolConflict[] | null;
|
|
6
|
+
//# sourceMappingURL=outline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outline.d.ts","sourceRoot":"","sources":["../../src/lib/outline.ts"],"names":[],"mappings":"AAUA,OAAO,EAAkB,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAShF,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,GAAG,IAAI,CAiChF;AAED,8GAA8G;AAC9G,wBAAgB,oBAAoB,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAChC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,MAAM,GACrB,cAAc,EAAE,GAAG,IAAI,CAMzB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* outline.ts — spike: symbol extraction for non-TS languages via `ast-grep outline`.
|
|
3
|
+
*
|
|
4
|
+
* Optional: needs the `ast-grep` binary on PATH. Returns null when it is missing
|
|
5
|
+
* or fails, so callers can skip (never conclude "no conflicts" from null).
|
|
6
|
+
*/
|
|
7
|
+
import { execFileSync } from 'child_process';
|
|
8
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'fs';
|
|
9
|
+
import { tmpdir } from 'os';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { compareSymbols } from './ast.js';
|
|
12
|
+
/** ext includes the dot, e.g. '.py'. Only function/class/method symbols are kept. */
|
|
13
|
+
export function outlineSymbols(content, ext) {
|
|
14
|
+
const dir = mkdtempSync(join(tmpdir(), 'spidersan-outline-'));
|
|
15
|
+
try {
|
|
16
|
+
const file = join(dir, `f${ext}`);
|
|
17
|
+
writeFileSync(file, content);
|
|
18
|
+
const out = execFileSync('ast-grep', ['outline', '--json=compact', file], {
|
|
19
|
+
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
20
|
+
});
|
|
21
|
+
const files = JSON.parse(out);
|
|
22
|
+
// `[]` = ast-grep doesn't know this file type; that is "unknown", not "no symbols"
|
|
23
|
+
if (!Array.isArray(files) || files.length === 0)
|
|
24
|
+
return null;
|
|
25
|
+
const items = files[0].items ?? [];
|
|
26
|
+
const bytes = Buffer.from(content);
|
|
27
|
+
const symbols = [];
|
|
28
|
+
const add = (it, parent) => {
|
|
29
|
+
const type = it.symbolType;
|
|
30
|
+
if (type !== 'function' && type !== 'class' && type !== 'method')
|
|
31
|
+
return;
|
|
32
|
+
symbols.push({
|
|
33
|
+
name: it.name, type,
|
|
34
|
+
startLine: it.range.start.line + 1, endLine: it.range.end.line + 1,
|
|
35
|
+
...(parent ? { parent } : {}),
|
|
36
|
+
// byteOffset, not line slicing: exact text of the symbol
|
|
37
|
+
content: bytes.subarray(it.range.byteOffset.start, it.range.byteOffset.end).toString('utf-8'),
|
|
38
|
+
});
|
|
39
|
+
it.members?.forEach(m => add(m, it.name));
|
|
40
|
+
};
|
|
41
|
+
items.forEach(it => add(it));
|
|
42
|
+
return symbols;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
rmSync(dir, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Same contract as ASTParser.findSymbolConflicts, for any language ast-grep outlines. null = unavailable. */
|
|
52
|
+
export function findOutlineConflicts(ext, contentA, labelA, contentB, labelB, baseContent) {
|
|
53
|
+
const a = outlineSymbols(contentA, ext);
|
|
54
|
+
const b = outlineSymbols(contentB, ext);
|
|
55
|
+
const base = baseContent === undefined ? undefined : outlineSymbols(baseContent, ext);
|
|
56
|
+
if (!a || !b || base === null)
|
|
57
|
+
return null;
|
|
58
|
+
return compareSymbols(a, labelA, b, labelB, base);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=outline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outline.js","sourceRoot":"","sources":["../../src/lib/outline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pushed-branch rows in spider_registries — the reader side.
|
|
3
|
+
*
|
|
4
|
+
* The `spidersan-webhook` edge function (treebird repo) registers every branch pushed
|
|
5
|
+
* to a watched repo, whoever pushed it (Codex, Copilot, a cloud agent, the web editor,
|
|
6
|
+
* a person), under one virtual machine, `github-push`. The write stays server-side:
|
|
7
|
+
* GitHub's HMAC proves the event and the service key never leaves Supabase.
|
|
8
|
+
*
|
|
9
|
+
* Such a row is shadowed — dropped on read — whenever a real machine has the same
|
|
10
|
+
* branch, so a branch registered by the machine that pushed it never shows twice or
|
|
11
|
+
* conflicts with itself (see dropShadowedPushRows).
|
|
12
|
+
*/
|
|
13
|
+
import type { MachineRegistryView } from '../types/cloud.js';
|
|
14
|
+
export declare const PUSH_MACHINE_ID = "github-push";
|
|
15
|
+
/**
|
|
16
|
+
* Drop pushed-branch rows a real registration already covers. Applied on every read
|
|
17
|
+
* (pull) BEFORE the caller's own machine is excluded, so a branch the caller itself
|
|
18
|
+
* registered doesn't come back as "github/<branch>" and conflict with itself.
|
|
19
|
+
* `ownBranches` covers the race where the webhook beats the pusher's own registry-sync.
|
|
20
|
+
*/
|
|
21
|
+
export declare function dropShadowedPushRows(views: MachineRegistryView[], ownBranches?: Iterable<string>): MachineRegistryView[];
|
|
22
|
+
//# sourceMappingURL=push-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-registry.d.ts","sourceRoot":"","sources":["../../src/lib/push-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,EAAE,EAAE,WAAW,GAAE,QAAQ,CAAC,MAAM,CAAM,GAAG,mBAAmB,EAAE,CAS5H"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const PUSH_MACHINE_ID = 'github-push';
|
|
2
|
+
/**
|
|
3
|
+
* Drop pushed-branch rows a real registration already covers. Applied on every read
|
|
4
|
+
* (pull) BEFORE the caller's own machine is excluded, so a branch the caller itself
|
|
5
|
+
* registered doesn't come back as "github/<branch>" and conflict with itself.
|
|
6
|
+
* `ownBranches` covers the race where the webhook beats the pusher's own registry-sync.
|
|
7
|
+
*/
|
|
8
|
+
export function dropShadowedPushRows(views, ownBranches = []) {
|
|
9
|
+
const covered = new Set(ownBranches);
|
|
10
|
+
for (const v of views) {
|
|
11
|
+
if (v.machine_id === PUSH_MACHINE_ID)
|
|
12
|
+
continue;
|
|
13
|
+
for (const b of v.branches)
|
|
14
|
+
if (b.status === 'active')
|
|
15
|
+
covered.add(b.name);
|
|
16
|
+
}
|
|
17
|
+
return views
|
|
18
|
+
.map(v => v.machine_id !== PUSH_MACHINE_ID ? v : { ...v, branches: v.branches.filter(b => !covered.has(b.name)) })
|
|
19
|
+
.filter(v => v.machine_id !== PUSH_MACHINE_ID || v.branches.length > 0);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=push-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push-registry.js","sourceRoot":"","sources":["../../src/lib/push-registry.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAA4B,EAAE,cAAgC,EAAE;IACjG,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,WAAW,CAAC,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,CAAC,UAAU,KAAK,eAAe;YAAE,SAAS;QAC/C,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ;YAAE,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,KAAK;SACP,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;SACjH,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAChF,CAAC"}
|