@sporhq/spor 0.16.0 → 0.18.0
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/.claude-plugin/plugin.json +1 -1
- package/GRAPH.md +29 -11
- package/adapters/README.md +1 -1
- package/adapters/codex/README.md +19 -3
- package/adapters/codex/hooks.json +4 -4
- package/adapters/copilot/spor.json +3 -3
- package/adapters/cursor/hooks.json +3 -3
- package/adapters/gemini/README.md +3 -3
- package/adapters/gemini/hooks/hooks.json +4 -4
- package/adapters/opencode/README.md +2 -2
- package/adapters/opencode/spor.js +11 -9
- package/bin/spor-hook.cmd +1 -0
- package/bin/spor-hook.js +4 -2
- package/bin/spor.cmd +1 -0
- package/bin/spor.js +312 -52
- package/bin/substrate-hook.cmd +1 -0
- package/bin/substrate-hook.js +8 -3
- package/hooks/hooks.json +5 -5
- package/lib/config.js +22 -2
- package/lib/history.js +8 -5
- package/lib/kernel/graph.js +2 -1
- package/lib/kernel/queue.js +7 -1
- package/lib/kernel/resolution.js +5 -0
- package/lib/query.js +16 -14
- package/lib/remote.js +48 -22
- package/lib/seed/schema-capture-pending.md +8 -7
- package/lib/seed/schema-edge-member-of-org.md +28 -0
- package/lib/seed/schema-issue.md +1 -1
- package/lib/seed/schema-organization.md +44 -0
- package/lib/seed/schema-person.md +35 -4
- package/lib/seed/schema-task.md +1 -1
- package/package.json +1 -1
- package/prompts/client/distill-local.md +3 -0
- package/scripts/engines/distill.js +1 -1
- package/scripts/engines/post-tool.js +5 -3
- package/scripts/engines/prompt-context.js +109 -4
- package/scripts/engines/util.js +32 -7
- package/skills/next/SKILL.md +2 -2
- package/skills/spor/references/concepts.md +3 -1
- package/skills/triage/SKILL.md +2 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "spor",
|
|
3
3
|
"displayName": "Spor Context Compiler",
|
|
4
4
|
"description": "Maintains a typed, versioned knowledge graph and compiles compact briefings from it: session-start injection, per-prompt relevance digests, capture at discovery, end-of-session distillation, decision queue.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.18.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "losthammer"
|
|
8
8
|
}
|
package/GRAPH.md
CHANGED
|
@@ -109,7 +109,8 @@ Rules:
|
|
|
109
109
|
| briefing | `brief-` | a compiled briefing (output of this system; never traversed) |
|
|
110
110
|
| correction | `corr-` | standing fix to a briefing: pin/exclude/guidance (never traversed) |
|
|
111
111
|
| question | `question-` | a routed ask the graph could not answer (queueable; status `open`/`answered`, gated) |
|
|
112
|
-
| person | `person-` | a member of the org — the identity anchor for `$viewer` binding and Tier-2 question routing (team mode; see "People, routing, and onboarding") |
|
|
112
|
+
| person | `person-` | a member of the org — mutable display name plus the identity anchor for `$viewer` binding and Tier-2 question routing (team mode; see "People, routing, and onboarding") |
|
|
113
|
+
| organization | `org-` | a durable organization identity anchor; people connect with `member-of-org` for membership and `stewards` for org-admin authority (`org-root` remains the virtual graph-wide operator anchor) |
|
|
113
114
|
| agent | `agent-` | a person-owned automation principal — a dispatched session's durable identity, owned by a person via an `owned-by` edge; its writes attribute "agent on behalf of person" (see "Agents") |
|
|
114
115
|
| profile | `profile-`| a reusable runtime+capability bundle an agent runs under: `harness`, `model`, `skills`/`plugins`/`mcp`. Its runtime fields ARE the dispatch satisfiability spec; `capturable: false` (see "The agent orchestration layer") |
|
|
115
116
|
| routine | `routine-`| owner-scoped trigger→action automation (`owned-by` a person): declarative `when → do` rules over graph events that dispatch only the owner's agents, AND-ed with org policy; `capturable: false` (see "The agent orchestration layer") |
|
|
@@ -571,14 +572,25 @@ date: 2026-06-13
|
|
|
571
572
|
Team mode (API.md) adds people to the graph. A `person` node is the org
|
|
572
573
|
member's identity anchor — every authenticated token's canonical subject is a
|
|
573
574
|
`person-` node, and `{name, email}` attribution resolves *from that node at
|
|
574
|
-
read time* (API.md §4).
|
|
575
|
-
|
|
576
|
-
|
|
575
|
+
read time* (API.md §4). `name` is the mutable user-facing display label; clients
|
|
576
|
+
render `name || title || email || id`, leaving the opaque `person-…` id as the
|
|
577
|
+
stable machine reference for graph edges, URLs, filters, and token subjects.
|
|
578
|
+
Tier-2 question routing and `$viewer`-scoped views (your queue, your mutes,
|
|
579
|
+
"what am I blocking") all key off the person node the caller's token is bound
|
|
580
|
+
to.
|
|
581
|
+
|
|
582
|
+
On a shared identity front door, organization authority is also graph-native.
|
|
583
|
+
Each org slug has a durable `organization` node (`org-<slug>`, carrying
|
|
584
|
+
`slug: <slug>`). A person's `member-of-org -> org-<slug>` edge records membership;
|
|
585
|
+
an additional `stewards -> org-<slug>` edge records org-admin authority.
|
|
586
|
+
`stewards -> org-root` keeps its distinct graph-wide operator meaning. Provider
|
|
587
|
+
roles, token bits, and email-domain mappings do not confer either relation.
|
|
577
588
|
|
|
578
589
|
```markdown
|
|
579
590
|
---
|
|
580
591
|
id: person-anthony
|
|
581
592
|
type: person
|
|
593
|
+
name: Anthony Allen
|
|
582
594
|
title: Anthony Allen
|
|
583
595
|
summary: Maintainer; stewards the schema registry and the hook engines.
|
|
584
596
|
email: losthammer@gmail.com
|
|
@@ -635,15 +647,19 @@ edges:
|
|
|
635
647
|
Joining someone to a team graph is three deliberate steps — do all three or
|
|
636
648
|
routing degrades quietly:
|
|
637
649
|
|
|
638
|
-
1. **Author the `person
|
|
639
|
-
the
|
|
650
|
+
1. **Author the `person-…` node** (frontmatter above), using the stable canonical
|
|
651
|
+
subject chosen by the org. Prefer the CLI/admin minting paths, which default
|
|
652
|
+
new ids to opaque email-derived subjects instead of mutable display-name
|
|
653
|
+
slugs. Set `name` to the person's display label and `email` to the address
|
|
654
|
+
the member commits and authenticates under.
|
|
640
655
|
2. **Add `stewards` edges** from that node to the areas/specs/norms they own.
|
|
641
656
|
Without at least one steward in a topic's neighborhood, questions there route
|
|
642
657
|
to no one and fall back to surfacing for everybody — answerable, but not
|
|
643
658
|
*directed*.
|
|
644
|
-
3. **Mint a token bound to the node:** an admin runs `spor
|
|
645
|
-
person
|
|
646
|
-
REST (admin-only; API.md §3/§4). The
|
|
659
|
+
3. **Mint a token bound to the node:** an admin runs `spor invite --person
|
|
660
|
+
person-…` / `spor invite --name <name> --email <email>`, or
|
|
661
|
+
`POST /v1/admin/tokens {person}` over REST (admin-only; API.md §3/§4). The
|
|
662
|
+
plaintext token is returned once.
|
|
647
663
|
|
|
648
664
|
If a member is reading and writing but **never receives routed questions, sees
|
|
649
665
|
an empty personal queue, or has no mutes take effect**, their token did not bind
|
|
@@ -833,6 +849,7 @@ assign that work to a person.
|
|
|
833
849
|
| `relates-to` | 0.5 | weak association |
|
|
834
850
|
| `mentions` | 0.5 | weakest association |
|
|
835
851
|
| `stewards` | 0.4 | this person stewards an area/spec/norm — the Tier-2 question-routing key |
|
|
852
|
+
| `member-of-org` | 0.3 | this person is a member of the target organization (inverse `has-org-member`); structural identity binding, not admin authority |
|
|
836
853
|
| `grouped-under` | 0.3 | this repo's home project grouping (inverse `groups`); structural membership, not work dependency |
|
|
837
854
|
| `owned-by` | 0.3 | this agent is owned by that person (inverse `owns`); structural identity binding, not work dependency |
|
|
838
855
|
| `uses-profile` | 0.3 | this agent's default profile (the runtime+capability bundle it dispatches under); structural config binding, overridable per assignment/dispatch |
|
|
@@ -841,11 +858,12 @@ assign that work to a person.
|
|
|
841
858
|
| `compiled-for` | — | briefing → its task/query (provenance only) |
|
|
842
859
|
| `shaped-by` | — | briefing → corrections applied (provenance only) |
|
|
843
860
|
|
|
844
|
-
`answers`, `assigned`, `stewards`, and `routed-to` are
|
|
861
|
+
`answers`, `assigned`, `stewards`, `member-of-org`, and `routed-to` are person-graph
|
|
845
862
|
edges of Tier-2 question routing; they ship in the seed pack and are
|
|
846
863
|
documented under "People, routing, and onboarding" above. `assigned` and
|
|
847
864
|
`routed-to` point at a `person-` node; `stewards` points from a person to
|
|
848
|
-
the area they own; `
|
|
865
|
+
the area or organization they own; `member-of-org` points from a person to an
|
|
866
|
+
`organization` identity anchor; `answers` points from any answer node back at the
|
|
849
867
|
`question-` it resolves.
|
|
850
868
|
|
|
851
869
|
`review-requested`, `reviewed-by`, and `changes-requested-by` are the
|
package/adapters/README.md
CHANGED
|
@@ -13,7 +13,7 @@ The Spor client is a portable core behind per-host adapters
|
|
|
13
13
|
|
|
14
14
|
- **Core**: the four hook engines in `scripts/` (session-start briefing,
|
|
15
15
|
prompt-context digest, post-tool journal, distill capture) and the
|
|
16
|
-
`bin/spor-hook` dispatcher that normalizes host payload quirks and
|
|
16
|
+
`bin/spor-hook.js` dispatcher that normalizes host payload quirks and
|
|
17
17
|
envelope event names. All engines are fail-open: any failure injects
|
|
18
18
|
nothing and exits 0.
|
|
19
19
|
- **Universal surfaces**: the server's MCP endpoint (`/mcp`) and REST
|
package/adapters/codex/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Codex's hooks are a near-clone of Claude Code's (same JSON-over-stdin payload
|
|
4
4
|
fields, same `hookSpecificOutput.additionalContext` envelope), so this adapter
|
|
5
|
-
is just a manifest over `bin/spor-hook`.
|
|
5
|
+
is just a manifest over `bin/spor-hook.js`.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -18,8 +18,24 @@ is just a manifest over `bin/spor-hook`.
|
|
|
18
18
|
(Or merge into an existing `~/.codex/hooks.json` / `[hooks]` table in
|
|
19
19
|
`~/.codex/config.toml`. Per-repo installs go in `<repo>/.codex/hooks.json`.)
|
|
20
20
|
|
|
21
|
-
3.
|
|
22
|
-
|
|
21
|
+
3. Add the backfill custom agent:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
mkdir -p ~/.codex/agents
|
|
25
|
+
{
|
|
26
|
+
printf 'name = "spor-backfill"\n'
|
|
27
|
+
printf 'description = "Populate or extend a project'\''s Spor graph (its nodes/ dir) from existing sources -- git history, issue/plan docs, specs, or external trackers (GitHub/Jira/Confluence via gh CLI or MCP tools). Use when bootstrapping a graph from scratch or importing a new source into an existing graph."\n'
|
|
28
|
+
printf 'developer_instructions = """\n'
|
|
29
|
+
awk 'BEGIN { n = 0 } /^---$/ { n++; next } n >= 2 { print }' "$SPOR_ROOT/agents/backfill.md"
|
|
30
|
+
printf '"""\n'
|
|
31
|
+
} > ~/.codex/agents/spor-backfill.toml
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`spor install codex` performs both steps automatically and keeps the custom
|
|
35
|
+
agent sourced from `agents/backfill.md`.
|
|
36
|
+
|
|
37
|
+
4. Approve the hooks in Codex's `/hooks` trust prompt on first run.
|
|
38
|
+
5. Environment (the same variables on every host):
|
|
23
39
|
|
|
24
40
|
```sh
|
|
25
41
|
export SPOR_SERVER=https://spor.example.com # remote mode
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
{
|
|
5
5
|
"matcher": "startup|resume",
|
|
6
6
|
"hooks": [
|
|
7
|
-
{ "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook session-start --host codex", "timeout": 20, "statusMessage": "Loading Spor briefing" }
|
|
7
|
+
{ "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" session-start --host codex", "timeout": 20, "statusMessage": "Loading Spor briefing" }
|
|
8
8
|
]
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
"UserPromptSubmit": [
|
|
12
12
|
{
|
|
13
13
|
"hooks": [
|
|
14
|
-
{ "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook prompt-context --host codex", "timeout": 15 }
|
|
14
|
+
{ "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" prompt-context --host codex", "timeout": 15 }
|
|
15
15
|
]
|
|
16
16
|
}
|
|
17
17
|
],
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
{
|
|
20
20
|
"matcher": "*",
|
|
21
21
|
"hooks": [
|
|
22
|
-
{ "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook post-tool --host codex", "timeout": 10 }
|
|
22
|
+
{ "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" post-tool --host codex", "timeout": 10 }
|
|
23
23
|
]
|
|
24
24
|
}
|
|
25
25
|
],
|
|
26
26
|
"Stop": [
|
|
27
27
|
{
|
|
28
28
|
"hooks": [
|
|
29
|
-
{ "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook distill --host codex --debounce 900", "timeout": 10 }
|
|
29
|
+
{ "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" distill --host codex --debounce 900", "timeout": 10 }
|
|
30
30
|
]
|
|
31
31
|
}
|
|
32
32
|
]
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"hooks": {
|
|
4
4
|
"sessionStart": [
|
|
5
|
-
{ "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook agents-md", "timeoutSec": 20 }
|
|
5
|
+
{ "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" agents-md", "timeoutSec": 20 }
|
|
6
6
|
],
|
|
7
7
|
"postToolUse": [
|
|
8
|
-
{ "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook post-tool --host copilot", "timeoutSec": 10 }
|
|
8
|
+
{ "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" post-tool --host copilot", "timeoutSec": 10 }
|
|
9
9
|
],
|
|
10
10
|
"agentStop": [
|
|
11
|
-
{ "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook distill --host copilot --debounce 900", "timeoutSec": 10 }
|
|
11
|
+
{ "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" distill --host copilot --debounce 900", "timeoutSec": 10 }
|
|
12
12
|
]
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"hooks": {
|
|
4
4
|
"sessionStart": [
|
|
5
|
-
{ "command": "__SPOR_ROOT__/bin/spor-hook session-start --host cursor", "timeout": 20 }
|
|
5
|
+
{ "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" session-start --host cursor", "timeout": 20 }
|
|
6
6
|
],
|
|
7
7
|
"afterFileEdit": [
|
|
8
|
-
{ "command": "__SPOR_ROOT__/bin/spor-hook post-tool --host cursor", "timeout": 10 }
|
|
8
|
+
{ "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" post-tool --host cursor", "timeout": 10 }
|
|
9
9
|
],
|
|
10
10
|
"sessionEnd": [
|
|
11
|
-
{ "command": "__SPOR_ROOT__/bin/spor-hook distill --host cursor", "timeout": 300 }
|
|
11
|
+
{ "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" distill --host cursor", "timeout": 300 }
|
|
12
12
|
]
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Gemini CLI's hooks use the same JSON-over-stdin payloads and the same
|
|
4
4
|
`hookSpecificOutput.additionalContext` envelope as Claude Code (it even ships
|
|
5
5
|
a `CLAUDE_PROJECT_DIR` compatibility alias), so this adapter is a manifest
|
|
6
|
-
over `bin/spor-hook`. The only real differences: the per-prompt event is
|
|
6
|
+
over `bin/spor-hook.js`. The only real differences: the per-prompt event is
|
|
7
7
|
called `BeforeAgent` (the dispatcher echoes that name back in the envelope),
|
|
8
8
|
the post-tool event is `AfterTool`, and timeouts are in milliseconds.
|
|
9
9
|
|
|
@@ -51,7 +51,7 @@ export SPOR_NUDGE_CMD='gemini --model gemini-2.5-flash'
|
|
|
51
51
|
|
|
52
52
|
- Gemini transcripts are not Claude's JSONL shape; the distiller falls back
|
|
53
53
|
to a generic extractor (every nested `.text` string).
|
|
54
|
-
- Hook stdout must be pure JSON on Gemini; `bin/spor-hook` already
|
|
54
|
+
- Hook stdout must be pure JSON on Gemini; `bin/spor-hook.js` already
|
|
55
55
|
discards engine stderr and emits either one JSON object or nothing.
|
|
56
56
|
- For on-demand graph access, add the Spor MCP server to settings:
|
|
57
57
|
|
|
@@ -60,4 +60,4 @@ export SPOR_NUDGE_CMD='gemini --model gemini-2.5-flash'
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
- `GEMINI.md` users: point `contextFileName` at `AGENTS.md` and run
|
|
63
|
-
`
|
|
63
|
+
`spor-hook agents-md` to get the standing briefing without hooks.
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"SessionStart": [
|
|
4
4
|
{
|
|
5
5
|
"hooks": [
|
|
6
|
-
{ "name": "spor-session-start", "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook session-start --host gemini", "timeout": 20000 }
|
|
6
|
+
{ "name": "spor-session-start", "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" session-start --host gemini", "timeout": 20000 }
|
|
7
7
|
]
|
|
8
8
|
}
|
|
9
9
|
],
|
|
10
10
|
"BeforeAgent": [
|
|
11
11
|
{
|
|
12
12
|
"hooks": [
|
|
13
|
-
{ "name": "spor-prompt-context", "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook prompt-context --host gemini", "timeout": 15000 }
|
|
13
|
+
{ "name": "spor-prompt-context", "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" prompt-context --host gemini", "timeout": 15000 }
|
|
14
14
|
]
|
|
15
15
|
}
|
|
16
16
|
],
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
{
|
|
19
19
|
"matcher": "write_file|replace",
|
|
20
20
|
"hooks": [
|
|
21
|
-
{ "name": "spor-post-tool", "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook post-tool --host gemini", "timeout": 10000 }
|
|
21
|
+
{ "name": "spor-post-tool", "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" post-tool --host gemini", "timeout": 10000 }
|
|
22
22
|
]
|
|
23
23
|
}
|
|
24
24
|
],
|
|
25
25
|
"SessionEnd": [
|
|
26
26
|
{
|
|
27
27
|
"hooks": [
|
|
28
|
-
{ "name": "spor-distill", "type": "command", "command": "__SPOR_ROOT__/bin/spor-hook distill --host gemini", "timeout": 300000 }
|
|
28
|
+
{ "name": "spor-distill", "type": "command", "command": "node \"__SPOR_ROOT__/bin/spor-hook.js\" distill --host gemini", "timeout": 300000 }
|
|
29
29
|
]
|
|
30
30
|
}
|
|
31
31
|
]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# OpenCode adapter
|
|
2
2
|
|
|
3
3
|
OpenCode has no command-hook system — plugins are in-process JS — so this
|
|
4
|
-
adapter is a single zero-dependency plugin file (`spor.js`) that
|
|
5
|
-
|
|
4
|
+
adapter is a single zero-dependency plugin file (`spor.js`) that launches
|
|
5
|
+
`bin/spor-hook.js` with Claude-shaped payloads. Same core, same
|
|
6
6
|
fail-open behavior as every other host.
|
|
7
7
|
|
|
8
8
|
## Install
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
//
|
|
3
3
|
// OpenCode has no command-hook system; plugins are in-process JS. This one
|
|
4
4
|
// stays a thin shim over the same portable core every other host uses: each
|
|
5
|
-
// hook synthesizes a Claude-shaped payload and
|
|
6
|
-
// bin/spor-hook, so briefing/digest/journal/distill behavior is
|
|
5
|
+
// hook synthesizes a Claude-shaped payload and launches the Node dispatcher
|
|
6
|
+
// (bin/spor-hook.js), so briefing/digest/journal/distill behavior is
|
|
7
7
|
// identical across hosts.
|
|
8
8
|
//
|
|
9
9
|
// chat.message -> session-start briefing (first message of a session)
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// nothing. Zero dependencies: node builtins only.
|
|
19
19
|
//
|
|
20
20
|
// Install: symlink this file into ~/.config/opencode/plugins/ (the symlink is
|
|
21
|
-
// resolved to find bin/spor-hook), or copy it and set SPOR_ROOT (legacy
|
|
21
|
+
// resolved to find bin/spor-hook.js), or copy it and set SPOR_ROOT (legacy
|
|
22
22
|
// SUBSTRATE_ROOT still read).
|
|
23
23
|
|
|
24
24
|
import { spawn } from "node:child_process"
|
|
@@ -36,18 +36,20 @@ const GRAPH =
|
|
|
36
36
|
? join(homedir(), ".spor")
|
|
37
37
|
: join(homedir(), ".substrate"))
|
|
38
38
|
const DEBOUNCE = process.env.SPOR_DEBOUNCE || process.env.SUBSTRATE_DEBOUNCE || "900"
|
|
39
|
+
const EMBEDDED_ROOT = "__SPOR_ROOT__"
|
|
39
40
|
|
|
40
41
|
function findDispatcher() {
|
|
41
42
|
const candidates = []
|
|
42
|
-
const root =
|
|
43
|
+
const root =
|
|
44
|
+
process.env.SPOR_ROOT ||
|
|
45
|
+
process.env.SUBSTRATE_ROOT ||
|
|
46
|
+
(EMBEDDED_ROOT !== "__SPOR_ROOT__" ? EMBEDDED_ROOT : "")
|
|
43
47
|
if (root) {
|
|
44
|
-
candidates.push(join(root, "bin", "spor-hook"))
|
|
45
|
-
candidates.push(join(root, "bin", "substrate-hook"))
|
|
48
|
+
candidates.push(join(root, "bin", "spor-hook.js"))
|
|
46
49
|
}
|
|
47
50
|
try {
|
|
48
51
|
const here = dirname(realpathSync(fileURLToPath(import.meta.url)))
|
|
49
|
-
candidates.push(join(here, "..", "..", "bin", "spor-hook"))
|
|
50
|
-
candidates.push(join(here, "..", "..", "bin", "substrate-hook"))
|
|
52
|
+
candidates.push(join(here, "..", "..", "bin", "spor-hook.js"))
|
|
51
53
|
} catch {}
|
|
52
54
|
return candidates.find((c) => existsSync(c)) || null
|
|
53
55
|
}
|
|
@@ -59,7 +61,7 @@ function run(bin, args, payload, timeoutMs) {
|
|
|
59
61
|
let done = false
|
|
60
62
|
const finish = (out) => { if (!done) { done = true; resolve(out) } }
|
|
61
63
|
try {
|
|
62
|
-
const child = spawn(
|
|
64
|
+
const child = spawn(process.execPath, [bin, ...args], { stdio: ["pipe", "pipe", "ignore"] })
|
|
63
65
|
let out = ""
|
|
64
66
|
const timer = setTimeout(() => { try { child.kill("SIGKILL") } catch {}; finish("") }, timeoutMs)
|
|
65
67
|
child.stdout.on("data", (d) => { out += d })
|
package/bin/spor-hook.cmd
CHANGED
package/bin/spor-hook.js
CHANGED
|
@@ -248,7 +248,9 @@ function logCrash(err) {
|
|
|
248
248
|
if (require.main === module) {
|
|
249
249
|
main()
|
|
250
250
|
.catch((err) => logCrash(err))
|
|
251
|
-
.finally(() =>
|
|
251
|
+
.finally(() => {
|
|
252
|
+
process.exitCode = 0;
|
|
253
|
+
});
|
|
252
254
|
}
|
|
253
255
|
|
|
254
|
-
module.exports = { logCrash };
|
|
256
|
+
module.exports = { logCrash, main };
|
package/bin/spor.cmd
CHANGED