@idleware/ringi 0.6.1 → 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.
- package/README.md +7 -18
- package/package.json +1 -1
- package/src/main/resources/profiles/a2a-1.json +0 -4
- package/src/main/resources/skill/ringi/SKILL.md +2 -18
- package/target/dts/adapters/host-posix/index.d.ts +2 -2
- package/target/dts/adapters/transport-git/branches.d.ts +2 -3
- package/target/dts/adapters/transport-git/index.d.ts +1 -2
- package/target/dts/adapters/transport-git/remote/admit.d.ts +2 -2
- package/target/dts/adapters/transport-git/remote/heartbeat.d.ts +2 -2
- package/target/dts/adapters/transport-git/remote/keys.d.ts +3 -3
- package/target/dts/adapters/transport-git/remote/types.d.ts +0 -1
- package/target/dts/adapters/transport-git/remote.d.ts +1 -1
- package/target/dts/adapters/transport-git/repo.d.ts +0 -1
- package/target/dts/adapters/transport-git/signing.d.ts +3 -3
- package/target/dts/clients/cli/agent/index.d.ts +0 -2
- package/target/dts/clients/cli/shared/index.d.ts +0 -1
- package/target/dts/core/agents/heartbeat/index.d.ts +2 -5
- package/target/dts/core/agents/heartbeat/pulse.d.ts +1 -3
- package/target/dts/core/agents/overview.d.ts +1 -5
- package/target/dts/core/agents/ports/host.d.ts +5 -37
- package/target/dts/core/agents/sessions/agent.d.ts +0 -17
- package/target/dts/core/agents/sessions/index.d.ts +0 -1
- package/target/dts/core/hub/history/index.d.ts +1 -11
- package/target/dts/core/hub/ports/store.d.ts +1 -5
- package/target/dts/core/hub/protocol/model.d.ts +0 -1
- package/target/dts/core/hub/protocol/reading.d.ts +0 -1
- package/target/dts/core/hub/rooms/index.d.ts +0 -4
- package/target/dts/core/hub/rooms/messages.d.ts +0 -1
- package/target/dts/core/hub/rooms/rooms.d.ts +1 -1
- package/target/dts/core/hub/signals/model.d.ts +0 -2
- package/target/dts/core/hub/usecases/hubs.d.ts +0 -1
- package/target/dts/core/hub/usecases/room.d.ts +0 -1
- package/target/dts/core/hub/usecases/rooms.d.ts +1 -5
- package/target/dts/core/hub/usecases/send.d.ts +0 -1
- package/target/dts/core/hub/usecases/view.d.ts +0 -22
- package/target/dts/core/hub/work/index.d.ts +0 -1
- package/target/dts/index.d.ts +0 -1
- package/target/esm/adapters/host-posix/index.js +30 -32
- package/target/esm/adapters/transport-git/branches.js +13 -20
- package/target/esm/adapters/transport-git/index.js +8 -9
- package/target/esm/adapters/transport-git/marks.js +4 -10
- package/target/esm/adapters/transport-git/remote/admit.js +8 -10
- package/target/esm/adapters/transport-git/remote/heartbeat-push.js +1 -1
- package/target/esm/adapters/transport-git/remote/heartbeat.js +4 -4
- package/target/esm/adapters/transport-git/remote/keys.js +7 -32
- package/target/esm/adapters/transport-git/remote/main.js +1 -4
- package/target/esm/adapters/transport-git/remote.js +1 -1
- package/target/esm/adapters/transport-git/repo.js +5 -3
- package/target/esm/adapters/transport-git/signing.js +12 -17
- package/target/esm/adapters/transport-git/transport.js +6 -6
- package/target/esm/clients/cli/agent/index.js +4 -10
- package/target/esm/clients/cli/arg-parse.js +1 -1
- package/target/esm/clients/cli/commands.js +6 -7
- package/target/esm/clients/cli/help.js +6 -13
- package/target/esm/clients/cli/operator/heartbeat-format.js +1 -1
- package/target/esm/clients/cli/operator/hub-commands.js +1 -1
- package/target/esm/clients/cli/operator/index.js +1 -1
- package/target/esm/clients/cli/operator/room.js +46 -7
- package/target/esm/clients/cli/operator/where-format.js +1 -3
- package/target/esm/clients/cli/shared/index.js +1 -2
- package/target/esm/clients/cli/shared/messages.js +1 -1
- package/target/esm/clients/cli/shared/profile.js +2 -2
- package/target/esm/clients/cli/shared/work.js +1 -1
- package/target/esm/composition/index.js +1 -3
- package/target/esm/core/agents/heartbeat/index.js +16 -23
- package/target/esm/core/agents/heartbeat/pulse.js +18 -33
- package/target/esm/core/agents/heartbeat/tools.js +1 -2
- package/target/esm/core/agents/overview.js +5 -6
- package/target/esm/core/agents/ports/host.js +2 -2
- package/target/esm/core/agents/sessions/agent.js +3 -23
- package/target/esm/core/agents/sessions/binding.js +5 -7
- package/target/esm/core/agents/sessions/index.js +3 -7
- package/target/esm/core/agents/sessions/join.js +1 -5
- package/target/esm/core/agents/sessions/resume.js +3 -4
- package/target/esm/core/agents/sessions/wait.js +10 -31
- package/target/esm/core/hub/capabilities.js +4 -2
- package/target/esm/core/hub/history/index.js +3 -1
- package/target/esm/core/hub/ports/store.js +3 -3
- package/target/esm/core/hub/protocol/index.js +3 -3
- package/target/esm/core/hub/protocol/model.js +2 -3
- package/target/esm/core/hub/protocol/reading.js +1 -3
- package/target/esm/core/hub/rooms/compat.js +1 -2
- package/target/esm/core/hub/rooms/hubs.js +2 -5
- package/target/esm/core/hub/rooms/index.js +3 -3
- package/target/esm/core/hub/rooms/messages.js +5 -10
- package/target/esm/core/hub/rooms/rooms.js +9 -38
- package/target/esm/core/hub/signals/effective.js +1 -21
- package/target/esm/core/hub/signals/index.js +2 -2
- package/target/esm/core/hub/signals/pins.js +1 -1
- package/target/esm/core/hub/usecases/hubs.js +0 -3
- package/target/esm/core/hub/usecases/room.js +2 -3
- package/target/esm/core/hub/usecases/rooms.js +4 -17
- package/target/esm/core/hub/usecases/roster.js +1 -1
- package/target/esm/core/hub/usecases/send.js +2 -12
- package/target/esm/core/hub/usecases/view.js +2 -6
- package/target/esm/core/hub/work/task.js +4 -11
- package/target/esm/index.js +1 -2
- package/target/esm/lib/json.js +1 -1
- package/target/dts/adapters/host-posix/group-state.d.ts +0 -7
- package/target/dts/adapters/host-posix/groups.d.ts +0 -1
- package/target/dts/adapters/host-posix/launch.d.ts +0 -7
- package/target/dts/adapters/host-posix/probes.d.ts +0 -5
- package/target/dts/adapters/host-posix/stats.d.ts +0 -9
- package/target/dts/adapters/transport-kafka/index.d.ts +0 -2
- package/target/dts/adapters/transport-temporal/index.d.ts +0 -2
- package/target/dts/clients/cli/operator/room-lifecycle.d.ts +0 -5
- package/target/dts/clients/cli/operator/room-list.d.ts +0 -5
- package/target/dts/clients/cli/operator/room-members.d.ts +0 -4
- package/target/dts/clients/cli/operator/watch.d.ts +0 -2
- package/target/dts/clients/cli/shared/budget.d.ts +0 -7
- package/target/dts/core/agents/turns/budgets.d.ts +0 -5
- package/target/dts/core/agents/turns/dispatch.d.ts +0 -9
- package/target/dts/core/agents/turns/exhaustion.d.ts +0 -11
- package/target/dts/core/agents/turns/grants.d.ts +0 -4
- package/target/dts/core/agents/turns/index.d.ts +0 -4
- package/target/dts/core/agents/turns/limits.d.ts +0 -2
- package/target/dts/core/agents/turns/messages.d.ts +0 -10
- package/target/dts/core/agents/turns/pings.d.ts +0 -5
- package/target/dts/core/agents/turns/plan.d.ts +0 -3
- package/target/dts/core/agents/turns/poll.d.ts +0 -8
- package/target/dts/core/agents/turns/results.d.ts +0 -3
- package/target/dts/core/agents/turns/seat.d.ts +0 -2
- package/target/dts/core/agents/turns/types.d.ts +0 -59
- package/target/dts/core/hub/decisions/budget.d.ts +0 -15
- package/target/dts/core/hub/decisions/check.d.ts +0 -6
- package/target/dts/core/hub/decisions/index.d.ts +0 -5
- package/target/dts/core/hub/decisions/proposal.d.ts +0 -38
- package/target/dts/core/hub/decisions/usage.d.ts +0 -12
- package/target/dts/core/hub/usecases/budget.d.ts +0 -23
- package/target/dts/core/hub/usecases/propose.d.ts +0 -17
- package/target/esm/adapters/host-posix/group-state.js +0 -24
- package/target/esm/adapters/host-posix/groups.js +0 -43
- package/target/esm/adapters/host-posix/launch.js +0 -80
- package/target/esm/adapters/host-posix/probes.js +0 -66
- package/target/esm/adapters/host-posix/stats.js +0 -27
- package/target/esm/adapters/transport-kafka/index.js +0 -4
- package/target/esm/adapters/transport-temporal/index.js +0 -4
- package/target/esm/clients/cli/operator/room-lifecycle.js +0 -23
- package/target/esm/clients/cli/operator/room-list.js +0 -27
- package/target/esm/clients/cli/operator/room-members.js +0 -15
- package/target/esm/clients/cli/operator/watch.js +0 -15
- package/target/esm/clients/cli/shared/budget.js +0 -50
- package/target/esm/core/agents/turns/budgets.js +0 -48
- package/target/esm/core/agents/turns/dispatch.js +0 -40
- package/target/esm/core/agents/turns/exhaustion.js +0 -16
- package/target/esm/core/agents/turns/grants.js +0 -36
- package/target/esm/core/agents/turns/index.js +0 -33
- package/target/esm/core/agents/turns/limits.js +0 -11
- package/target/esm/core/agents/turns/messages.js +0 -32
- package/target/esm/core/agents/turns/pings.js +0 -30
- package/target/esm/core/agents/turns/plan.js +0 -29
- package/target/esm/core/agents/turns/poll.js +0 -48
- package/target/esm/core/agents/turns/results.js +0 -40
- package/target/esm/core/agents/turns/seat.js +0 -44
- package/target/esm/core/agents/turns/types.js +0 -3
- package/target/esm/core/hub/decisions/budget.js +0 -40
- package/target/esm/core/hub/decisions/check.js +0 -43
- package/target/esm/core/hub/decisions/index.js +0 -10
- package/target/esm/core/hub/decisions/proposal.js +0 -73
- package/target/esm/core/hub/decisions/usage.js +0 -39
- package/target/esm/core/hub/usecases/budget.js +0 -78
- package/target/esm/core/hub/usecases/propose.js +0 -39
|
@@ -1,47 +1,26 @@
|
|
|
1
|
-
// wait: an agent holds the line until something new arrives for it or
|
|
2
|
-
|
|
1
|
+
// wait: an agent holds the line until something new arrives for it, or at most `max` seconds (and never past 110, so
|
|
2
|
+
// a tool call that runs it is not cut off).
|
|
3
|
+
import { HIDDEN } from '../../hub/rooms/index.js';
|
|
3
4
|
import { fail, sleep } from '../../../lib/index.js';
|
|
4
|
-
import {
|
|
5
|
+
import { unread } from '../../hub/protocol/index.js';
|
|
5
6
|
import { loadRoom } from '../../hub/history/index.js';
|
|
6
7
|
import { bound } from './index.js';
|
|
7
8
|
import { inbox } from './agent.js';
|
|
8
9
|
export const wait = async (a, { max = 120, ...options } = {}) => {
|
|
9
10
|
if (!Number.isFinite(max) || max < 0)
|
|
10
11
|
fail('wait max must be nonnegative seconds');
|
|
11
|
-
const start = Date.now(),
|
|
12
|
-
let last = start, silence = Math.min(max, 110);
|
|
12
|
+
const start = Date.now(), until = start + Math.min(max, 110) * 1000;
|
|
13
13
|
for (;;) {
|
|
14
14
|
// Each poll renews the lease and checks stop, takeover, membership and the room's identity.
|
|
15
15
|
const current = bound();
|
|
16
16
|
if (current.R.genesis !== a.R.genesis || current.me !== a.me)
|
|
17
17
|
fail('the waiting session changed its binding');
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
if (unread(logs, me, st.seen ?? {}, Infinity, HIDDEN).msgs.length)
|
|
18
|
+
const { logs, st } = loadRoom(current.R, current.me);
|
|
19
|
+
if (unread(logs, current.me, st.seen ?? {}, Infinity, HIDDEN).msgs.length)
|
|
21
20
|
return { inbox: inbox(current, options), seconds: (Date.now() - start) / 1000 };
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
last = state.last;
|
|
26
|
-
silence = state.silence;
|
|
27
|
-
const now = Date.now();
|
|
28
|
-
if (now - last >= silence * 1000 || now - start >= max * 1000)
|
|
29
|
-
return { inbox: null, seconds: (now - start) / 1000 };
|
|
30
|
-
await sleep(Math.min(1000, max * 1000 - (now - start)));
|
|
21
|
+
if (Date.now() >= until)
|
|
22
|
+
return { inbox: null, seconds: (Date.now() - start) / 1000 };
|
|
23
|
+
await sleep(Math.min(1000, until - Date.now()));
|
|
31
24
|
}
|
|
32
25
|
};
|
|
33
|
-
const answerPings = (a, logs, state) => {
|
|
34
|
-
for (const m of logs.get(a.me) ?? [])
|
|
35
|
-
if (m.kind === 'pong' && m.reId)
|
|
36
|
-
state.ponged.add(m.reId);
|
|
37
|
-
for (const msgs of logs.values())
|
|
38
|
-
for (const m of msgs) {
|
|
39
|
-
if (m.kind !== 'ping' || m.bad || !m.to.includes(a.me) || state.ponged.has(m.id))
|
|
40
|
-
continue;
|
|
41
|
-
state.ponged.add(m.id);
|
|
42
|
-
state.last = Date.now();
|
|
43
|
-
state.silence = Math.max(state.silence, Number(m.trailers.next?.[0]) || 600);
|
|
44
|
-
append(a.R, a.me, pongText(a.me, m, 'is waiting'));
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
26
|
//# sourceMappingURL=wait.js.map
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// The capabilities this build enforces: `<name>/<major>` → the module whose folds apply it. A new major is a new module
|
|
2
2
|
// registered beside the old one. A hub contract selects at most one major of each name; a room applies what its
|
|
3
3
|
// contract base selects (docs/ARCHITECTURE.md, Evolution).
|
|
4
|
-
import * as decisions from './decisions/index.js';
|
|
5
4
|
import * as profiles from './work/index.js';
|
|
6
|
-
|
|
5
|
+
// decisions/1 (proposals, grants, budgets) is retired: nothing in this build creates, decides or spends what it
|
|
6
|
+
// governs, so a contract that still names it restricts nothing here and is read.
|
|
7
|
+
const RETIRED = {};
|
|
8
|
+
export const CAPABILITIES = { [profiles.PROFILES_CAPABILITY]: profiles, 'decisions/1': RETIRED };
|
|
7
9
|
// Why a contract's capability list cannot be enforced by this build, or nothing.
|
|
8
10
|
export const unenforceable = (ids) => {
|
|
9
11
|
const names = ids.map(id => id.split('/')[0]);
|
|
@@ -59,7 +59,9 @@ const acceptSignals = (R, logs, st, alarms) => {
|
|
|
59
59
|
// What follows from an alarm, by what it is about.
|
|
60
60
|
const remedy = (R, alarm) => {
|
|
61
61
|
if (alarm.startsWith('signal '))
|
|
62
|
-
return '
|
|
62
|
+
return alarm.endsWith('was removed after it was accepted')
|
|
63
|
+
? 'the messages stay the truth; its author restores it with ringi agent signals --repair'
|
|
64
|
+
: 'the messages stay the truth; the signal is ignored';
|
|
63
65
|
if (alarm.startsWith('commit '))
|
|
64
66
|
return 'it is skipped; the room reads on without it';
|
|
65
67
|
return `it is quarantined and withheld. Restore it, or the operator runs ringi forget ${R.name}/<log>`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// store: the port every storage type implements, each an adapter (adapters/transport-<type
|
|
2
|
-
//
|
|
1
|
+
// store: the port every storage type implements, each an adapter (adapters/transport-<type>) that composition/
|
|
2
|
+
// registers. Rooms, accepted history, profiles and signals use only this
|
|
3
3
|
// interface; git is the first implementation, and a hub's descriptor names which one it uses (`store.type`). A store
|
|
4
|
-
// keeps the shared contract (versioned files the participants agree on: contract.json,
|
|
4
|
+
// keeps the shared contract (versioned files the participants agree on: contract.json, profiles), the
|
|
5
5
|
// rooms (a control record with versioned files, members and roster, and one append-only log per member) and signals
|
|
6
6
|
// (named markers on log entries); the port is split by what each part does (Log, Attachments, …).
|
|
7
7
|
// Revisions are opaque, store-assigned ids. A store says whether one revision extends another; ringi builds the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// protocol: the message protocol, docs/spec/core-1.md (§3, §5
|
|
2
|
-
// turns its records into messages and hands them here: the text (text.ts), threads and order (threads.ts), inboxes
|
|
3
|
-
//
|
|
1
|
+
// protocol: the message protocol, docs/spec/core-1.md (§3, §5, §6), independent of storage and of any task model. A store
|
|
2
|
+
// turns its records into messages and hands them here: the text (text.ts), threads and order (threads.ts), inboxes
|
|
3
|
+
// (reading.ts). What a task is, which states it has and who may move it, is a profile, not part of the protocol.
|
|
4
4
|
export * from './model.js';
|
|
5
5
|
export * from './text.js';
|
|
6
6
|
export * from './threads.js';
|
|
@@ -8,8 +8,7 @@ export const TRAILER_RE = /^([A-Za-z][A-Za-z0-9-]*):[ \t]*(.*)$/;
|
|
|
8
8
|
export const TEXT_MAX = 64 * 1024;
|
|
9
9
|
// Trailers the protocol itself interprets; any other trailer belongs to an extension or a profile.
|
|
10
10
|
export const KNOWN = new Set(['to', 're', 'kind', 'state', 'hop', 'assign', 'roles']);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const HIDDEN = new Set(['ping', 'pong', 'usage', 'card', 'room']);
|
|
11
|
+
// Kinds that are bookkeeping, never delivered to an inbox: room records.
|
|
12
|
+
export const HIDDEN = new Set(['room']);
|
|
14
13
|
export const one = (m, k) => m.trailers[k]?.[0] ?? null;
|
|
15
14
|
//# sourceMappingURL=model.js.map
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
// reading: what is delivered to whom (core-1 §6)
|
|
1
|
+
// reading: what is delivered to whom (core-1 §6).
|
|
2
2
|
import { ordered } from './order.js';
|
|
3
3
|
const addressed = (m, id) => m.to.includes(id) || m.to.includes('*');
|
|
4
4
|
const hiddenKind = (m, hidden) => !!m.kind && hidden.has(m.kind);
|
|
5
5
|
// Delivered: well-formed, not a hidden kind, addressed to `id` by name or `*`.
|
|
6
6
|
export const visible = (m, id, hidden) => !m.invalid && !m.bad && !hiddenKind(m, hidden) && addressed(m, id);
|
|
7
|
-
// Wakes: only a direct address. Broadcasts are read when the agent is awake for another reason.
|
|
8
|
-
export const wakes = (m, id, hidden) => visible(m, id, hidden) && m.to.includes(id);
|
|
9
7
|
const after = (msgs, cursor) => msgs.slice(msgs.findIndex(m => m.id === cursor) + 1);
|
|
10
8
|
const others = (logs, id) => [...logs].filter(([log]) => log !== id);
|
|
11
9
|
// Each log's cursor moves past what was shown and what is not for `id`, up to the first message held back.
|
|
@@ -42,8 +42,7 @@ export const contractAt = (store, name, rev) => {
|
|
|
42
42
|
const why = refusal(c, name);
|
|
43
43
|
if (why)
|
|
44
44
|
fail(why);
|
|
45
|
-
|
|
46
|
-
return { ...c, authority: a === null ? {} : strictJson(a, `hub ${name}: authority.json at ${short(rev)}`) };
|
|
45
|
+
return c;
|
|
47
46
|
};
|
|
48
47
|
// Why this ringi cannot read the hub's store as it is laid out (`shown`: what its contract names, or what its store
|
|
49
48
|
// type finds there when the contract names nothing), or nothing; `reads` is what the store type reads.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// hubs: a hub's descriptor beside its store, the contract it carries, and opening it.
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import * as D from '../decisions/index.js';
|
|
5
4
|
import { DEFAULT_PROFILE, PROFILES_CAPABILITY } from '../work/index.js';
|
|
6
5
|
import { hubPorts } from '../ports/index.js';
|
|
7
6
|
import { fail } from '../../../lib/index.js';
|
|
@@ -15,17 +14,15 @@ const VERSION = () => hubPorts().build.version;
|
|
|
15
14
|
const HUB_MD = `# ringi hub
|
|
16
15
|
|
|
17
16
|
The shared contract of a ringi hub (docs/spec/hub-1.md): contract.json (schema, version, protocol, trust, capabilities, the
|
|
18
|
-
default profile)
|
|
19
|
-
profiles/ (what a piece of work is in a room: its states, and who may move it). Use \`ringi\`; never edit by hand.
|
|
17
|
+
default profile) and profiles/ (what a piece of work is in a room: its states, and who may move it). Use \`ringi\`; never edit by hand.
|
|
20
18
|
`;
|
|
21
19
|
export const readProfileResource = (name) => fs.readFileSync(path.join(hubPorts().build.resources, 'profiles', `${name}.json`), 'utf8');
|
|
22
20
|
// A new hub: local (asserted senders), or signed, with the operator's root key generated on this machine and pinned.
|
|
23
21
|
// The files of a new hub's first contract.
|
|
24
22
|
const genesisFiles = (d, layout) => {
|
|
25
|
-
const contract = { schema: 'ringi-hub', version: '1.2', protocol: PROTOCOL_MAJOR, trust: d.trust, store: layout, capabilities: [
|
|
23
|
+
const contract = { schema: 'ringi-hub', version: '1.2', protocol: PROTOCOL_MAJOR, trust: d.trust, store: layout, capabilities: [PROFILES_CAPABILITY], profile: DEFAULT_PROFILE, ...(d.root ? { root: d.root } : {}) };
|
|
26
24
|
return {
|
|
27
25
|
'contract.json': JSON.stringify(contract, null, 2) + '\n',
|
|
28
|
-
'authority.json': JSON.stringify({ decide: {} }, null, 2) + '\n',
|
|
29
26
|
'HUB.md': HUB_MD,
|
|
30
27
|
[`profiles/${DEFAULT_PROFILE}.json`]: readProfileResource(DEFAULT_PROFILE),
|
|
31
28
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// hub: hubs and rooms on top of any store. A hub is a directory with a descriptor (hub.json: which store, where,
|
|
2
|
-
// what this machine pins) beside its store. Its shared contract lives in the store (contract.json,
|
|
3
|
-
//
|
|
2
|
+
// what this machine pins) beside its store. Its shared contract lives in the store (contract.json, profiles/*.json).
|
|
3
|
+
// hubs.ts reads descriptors and contracts, rooms.ts opens rooms with their profile,
|
|
4
4
|
// messages.ts turns stored records into messages and writes them (docs/spec/hub-1.md).
|
|
5
5
|
import { CAPABILITIES } from '../capabilities.js';
|
|
6
6
|
// The root principal of every hub and room (hub-1): the operator of the machine that made it.
|
|
@@ -8,7 +8,7 @@ export const OPERATOR = 'operator';
|
|
|
8
8
|
export const HUB_MAJOR = 1;
|
|
9
9
|
export const PROTOCOL_MAJOR = 1;
|
|
10
10
|
export const CAPS = new Set(Object.keys(CAPABILITIES));
|
|
11
|
-
export const RESERVED_ROOMS = new Set(['main'
|
|
11
|
+
export const RESERVED_ROOMS = new Set(['main']);
|
|
12
12
|
// Whether the room's work has started: it has the quorum of agents its genesis asked for (`Quorum`), or asked for none.
|
|
13
13
|
export const start = (R) => {
|
|
14
14
|
const agents = Object.values(R.members).filter(m => m.role !== 'root').length;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// messages: stored records read as messages, and a message written with the signals it made.
|
|
2
|
-
import
|
|
3
|
-
import { compose, decodeText, link, parse } from '../protocol/index.js';
|
|
2
|
+
import { decodeText, link, parse } from '../protocol/index.js';
|
|
4
3
|
import { emit } from '../signals/write.js';
|
|
5
4
|
export const toMessage = (R, from, x, seq) => {
|
|
6
5
|
const trust = R.hub.signed ? 'verified' : 'asserted';
|
|
@@ -11,10 +10,9 @@ export const toMessage = (R, from, x, seq) => {
|
|
|
11
10
|
return { ...base, invalid: d.invalid, subject: '', body: '', to: [], re: null, kind: null, state: null, assign: null, roles: null, hop: 0, bad: d.invalid, trailers: {} };
|
|
12
11
|
}
|
|
13
12
|
const p = parse(d.text, s => R.hub.store.isRef(s));
|
|
14
|
-
//
|
|
15
|
-
const ext = D.check(p, s => R.hub.store.isRef(s));
|
|
13
|
+
// The room profile's states are part of a message's validity.
|
|
16
14
|
const state = p.state && !p.bad && !R.profile.states.includes(p.state) ? `State ${p.state} is not in profile ${R.profile.name}` : null;
|
|
17
|
-
return { ...base, invalid: null, ...p, bad: p.bad ??
|
|
15
|
+
return { ...base, invalid: null, ...p, bad: p.bad ?? state };
|
|
18
16
|
};
|
|
19
17
|
// The room as stored, read without a reader's lock or state: for an author working out the signals of its own message
|
|
20
18
|
// (append runs inside reader locks too). Readers check signals themselves.
|
|
@@ -22,10 +20,8 @@ const asStored = (R) => {
|
|
|
22
20
|
const s = R.hub.store.room(R.name) ?? R.s;
|
|
23
21
|
return link(new Map(R.hub.store.logIds(s).map(id => [id, R.hub.store.messages(s, id, R.hub.store.logHead(s, id)).map((x, seq) => toMessage(R, id, x, seq))])));
|
|
24
22
|
};
|
|
25
|
-
// Whether a message can make signals: it opens, assigns or moves work
|
|
26
|
-
const
|
|
27
|
-
const movesWork = (p, R) => p.kind === R.profile.opens || !!p.assign || !!p.state;
|
|
28
|
-
const CARRIES = (p, R) => !p.bad && (movesWork(p, R) || DECISION_KINDS.includes(p.kind ?? ''));
|
|
23
|
+
// Whether a message can make signals: it opens, assigns or moves work.
|
|
24
|
+
const CARRIES = (p, R) => !p.bad && (p.kind === R.profile.opens || !!p.assign || !!p.state);
|
|
29
25
|
// A message on the author's log, and the signals it made (core/hub/signals/write.ts).
|
|
30
26
|
export const append = (R, who, text, entries = []) => {
|
|
31
27
|
const id = R.hub.store.append(R.s, who, text, entries);
|
|
@@ -43,5 +39,4 @@ export const withSizes = (R, m) => {
|
|
|
43
39
|
const sizes = R.hub.store.sizes(m.id, m.files.map(f => f.path));
|
|
44
40
|
return m.files.map(f => ({ ...f, size: sizes.get(f.path) ?? null }));
|
|
45
41
|
};
|
|
46
|
-
export const pongText = (me, m, what) => compose(`pong: ${me} ${what}`, [['To', m.from], ['Re', m.id], ['Kind', 'pong'], ['Hop', String(m.hopEff + 1)]]);
|
|
47
42
|
//# sourceMappingURL=messages.js.map
|
|
@@ -1,48 +1,20 @@
|
|
|
1
|
-
// rooms: a room opened with its contract,
|
|
1
|
+
// rooms: a room opened with its contract, profile, members and quorum; and the root's control changes.
|
|
2
2
|
import { DEFAULT_PROFILE, parseProfile } from '../work/index.js';
|
|
3
3
|
import { locked } from '../ports/index.js';
|
|
4
4
|
import { fail, strictJson } from '../../../lib/index.js';
|
|
5
5
|
import { compose } from '../protocol/index.js';
|
|
6
6
|
import { readProfileResource } from './hubs.js';
|
|
7
7
|
import { contractAt } from './compat.js';
|
|
8
|
-
export const roomNames = (H
|
|
9
|
-
|
|
10
|
-
const missing = (H, name, quiet) => {
|
|
11
|
-
if (quiet)
|
|
12
|
-
return null;
|
|
13
|
-
const gone = roomNames(H, true).find(a => a.startsWith(`${name}@`));
|
|
14
|
-
return fail(gone ? `room ${name} is archived as ${gone}; read it with --room ${gone}` : `no room ${name} in hub ${H.name}; rooms: ${roomNames(H).join(', ') || 'none'}`);
|
|
15
|
-
};
|
|
8
|
+
export const roomNames = (H) => H.store.roomNames();
|
|
9
|
+
const missing = (H, name, quiet) => quiet ? null : fail(`no room ${name} in hub ${H.name}; rooms: ${roomNames(H).join(', ') || 'none'}`);
|
|
16
10
|
// The genesis names its room, in the root's hand: a history offered under another name is not that room.
|
|
17
11
|
const checkNamed = (s, name) => {
|
|
18
|
-
const named = s.archived ? name.slice(0, name.lastIndexOf('@')) : name;
|
|
19
12
|
const subject = s.control[0]?.rev === s.genesis ? s.control[0].text.split('\n')[0] : '(none)';
|
|
20
|
-
|
|
21
|
-
if (subject !== `room ${named}` || !archiveName)
|
|
13
|
+
if (subject !== `room ${name}`)
|
|
22
14
|
fail(`room ${name}: its genesis is "${subject}"; refusing a room's history under another name`);
|
|
23
15
|
};
|
|
24
|
-
// The contract
|
|
25
|
-
|
|
26
|
-
const contracts = (s) => {
|
|
27
|
-
const atControl = new Map();
|
|
28
|
-
let current = s.base;
|
|
29
|
-
for (const rec of s.control) {
|
|
30
|
-
current = /^Contract: ([0-9a-f]{40,64})$/m.exec(rec.text)?.[1] ?? current;
|
|
31
|
-
atControl.set(rec.rev, current);
|
|
32
|
-
}
|
|
33
|
-
return { atControl, current };
|
|
34
|
-
};
|
|
35
|
-
const authorityOf = (H, atControl) => {
|
|
36
|
-
const tables = new Map();
|
|
37
|
-
return control => {
|
|
38
|
-
const rev = control ? atControl.get(control) : undefined;
|
|
39
|
-
if (!rev)
|
|
40
|
-
return null;
|
|
41
|
-
if (!tables.has(rev))
|
|
42
|
-
tables.set(rev, contractAt(H.store, H.name, rev).authority);
|
|
43
|
-
return tables.get(rev);
|
|
44
|
-
};
|
|
45
|
-
};
|
|
16
|
+
// The contract the room follows: the hub contract it was created on, or the last one the operator moved it to.
|
|
17
|
+
const contractRev = (s) => s.control.reduce((current, rec) => /^Contract: ([0-9a-f]{40,64})$/m.exec(rec.text)?.[1] ?? current, s.base);
|
|
46
18
|
// The room's profile is fixed when the room is made: its content is copied into the genesis control entry, so no
|
|
47
19
|
// later change of the hub's profiles or contract reinterprets the room's history. Rooms made before this carry none
|
|
48
20
|
// and use the default profile of the contract they were created on.
|
|
@@ -61,11 +33,10 @@ export const openRoom = (H, name, { quiet = false } = {}) => {
|
|
|
61
33
|
if (!s)
|
|
62
34
|
return missing(H, name, quiet);
|
|
63
35
|
checkNamed(s, name);
|
|
64
|
-
const
|
|
36
|
+
const current = contractRev(s);
|
|
65
37
|
const contract = current === H.main ? H.contract : contractAt(H.store, H.name, current);
|
|
66
|
-
return { hub: H, s, name,
|
|
67
|
-
|
|
68
|
-
members: membersOf(H, s, name), quorum: quorumOf(s) };
|
|
38
|
+
return { hub: H, s, name, head: s.head, genesis: s.genesis, contractRev: current, contract,
|
|
39
|
+
profile: parseProfile(profileOf(H, s, name), `room ${name}: profile`), members: membersOf(H, s, name), quorum: quorumOf(s) };
|
|
69
40
|
};
|
|
70
41
|
export const room = (H, name) => openRoom(H, name);
|
|
71
42
|
export const ROOM_MD = (name) => `# room ${name}\n\nA ringi room: members.json lists who takes part; profile.json is what a piece of work is here, fixed when the room was made.\n`;
|
|
@@ -1,31 +1,11 @@
|
|
|
1
1
|
// effective: the signals a message made, judged on the room as it is now (thread order is fixed, so this does not
|
|
2
2
|
// change).
|
|
3
|
-
import * as D from '../decisions/index.js';
|
|
4
3
|
import { taskFor, taskOf } from '../work/index.js';
|
|
5
|
-
import { one } from '../protocol/index.js';
|
|
6
4
|
const isWorkSignal = (fx) => fx === 'opened' || fx === 'assigned' || fx.startsWith('state.');
|
|
7
5
|
// What the message did to its piece of work: opened, assigned, a state.
|
|
8
6
|
const workSignals = (R, all, m) => {
|
|
9
7
|
const work = taskFor(all, m, R.profile);
|
|
10
8
|
return work ? (taskOf(all, work, R.profile).effects.get(m.id) ?? []).filter(isWorkSignal) : [];
|
|
11
9
|
};
|
|
12
|
-
|
|
13
|
-
const ownSignals = (R, all, m) => [
|
|
14
|
-
...m.kind === 'proposal' ? ['proposed'] : [],
|
|
15
|
-
...m.kind === 'usage' && D.counted(all, R.authorityAt, m) ? [`usage.${one(m, 'usage')}`] : [],
|
|
16
|
-
];
|
|
17
|
-
// An answer to a proposal: a counted stamp, the decision, its revocation.
|
|
18
|
-
const answerSignals = (R, all, m) => {
|
|
19
|
-
const p = m.reId ? all.get(m.reId) : undefined;
|
|
20
|
-
if (p?.kind !== 'proposal' || p.bad)
|
|
21
|
-
return [];
|
|
22
|
-
const info = D.proposalOf(all, p, R.authorityAt);
|
|
23
|
-
const decided = m.kind === 'decision';
|
|
24
|
-
return [
|
|
25
|
-
...m.kind === 'stamp' && info.reviewers.includes(m.from) ? [`stamp.${one(m, 'stamp')}`] : [],
|
|
26
|
-
...decided && info.decision?.id === m.id ? [`decision.${info.decision.decision}`] : [],
|
|
27
|
-
...decided && info.revoked?.id === m.id ? ['decision.revoked'] : [],
|
|
28
|
-
];
|
|
29
|
-
};
|
|
30
|
-
export const effective = (R, all, m) => m.bad || m.invalid ? [] : [...workSignals(R, all, m), ...ownSignals(R, all, m), ...answerSignals(R, all, m)];
|
|
10
|
+
export const effective = (R, all, m) => m.bad || m.invalid ? [] : workSignals(R, all, m);
|
|
31
11
|
//# sourceMappingURL=effective.js.map
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
// after a crash between message and tag changes nothing and is restored with `signals --repair`.
|
|
12
12
|
//
|
|
13
13
|
// Marks are the second layer, in the same namespace: where attention is, not what was decided. `read` says a message
|
|
14
|
-
// reached its reader (made on delivery); `taken` says a member took a piece of work. A mark changes no state
|
|
15
|
-
//
|
|
14
|
+
// reached its reader (made on delivery); `taken` says a member took a piece of work. A mark changes no state
|
|
15
|
+
// or assignment. It is checked only for being possible: its target is an accepted message of the room, its
|
|
16
16
|
// actor a member; `read` only for a message addressed to the actor or to all, `taken` only for a message that opens
|
|
17
17
|
// work.
|
|
18
18
|
export * from './model.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// pins: a reader's pins, the stored object of each signal it accepted. A changed or vanished one is an alarm.
|
|
2
|
-
// A signal's identity in any store
|
|
2
|
+
// A signal's identity in any store: its tag's name `<actor>.<signal>.<message12>`.
|
|
3
3
|
const keyOf = (ref) => ref.split('/').at(-1);
|
|
4
4
|
export const pins = (records, checked, accepted = {}) => {
|
|
5
5
|
const now = new Map(records.map(x => [`${x.actor}.${x.name}`, x]));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// hubs: the operator's hubs on this machine: list, create, switch, add a signed replica, amend the shared contract.
|
|
2
|
-
import fs from 'node:fs';
|
|
3
2
|
import { DEFAULT_PROFILE, PROFILES_CAPABILITY, parseProfile } from '../work/index.js';
|
|
4
3
|
import { OPERATOR, config, createHub, hubNames, openHub, roomNames, setConfig, transportOf } from '../rooms/index.js';
|
|
5
4
|
import { hubPorts } from '../ports/index.js';
|
|
@@ -56,8 +55,6 @@ const setKeys = (contract, set) => {
|
|
|
56
55
|
// The files the amendment brings besides the contract; a profile is checked before it reaches the contract.
|
|
57
56
|
const extraFiles = (x) => {
|
|
58
57
|
const files = {};
|
|
59
|
-
if (x.authority)
|
|
60
|
-
files['authority.json'] = json(strictJson(fs.readFileSync(x.authority, 'utf8'), x.authority));
|
|
61
58
|
for (const f of x.files ?? []) {
|
|
62
59
|
if (/^profiles\/[^/]+\.json$/.test(f.path))
|
|
63
60
|
parseProfile(f.content.toString('utf8'), f.path);
|
|
@@ -53,10 +53,9 @@ export const files = ({ R, me }, ref, path) => {
|
|
|
53
53
|
return { m, files: m.files.filter(f => !path || f.path === path).map(f => ({ path: f.path, content: R.hub.store.file(m.id, f.path) })) };
|
|
54
54
|
};
|
|
55
55
|
export const members = ({ R, me }) => {
|
|
56
|
-
const { logs
|
|
57
|
-
const cards = new Map(ordered([...all.values()].filter(m => m.kind === 'card' && !m.bad)).map(m => [m.from, m]));
|
|
56
|
+
const { logs } = linked(R, me);
|
|
58
57
|
return Object.entries(R.members).map(([id, info]) => ({ id, role: info.role ?? 'member',
|
|
59
|
-
messages: (logs.get(id) ?? []).filter(m => !(m.kind && HIDDEN.has(m.kind))).length
|
|
58
|
+
messages: (logs.get(id) ?? []).filter(m => !(m.kind && HIDDEN.has(m.kind))).length }));
|
|
60
59
|
};
|
|
61
60
|
// --- signals ---
|
|
62
61
|
// The room's signals, each checked against its message and against what this reader accepted before; `repair` first
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// rooms: the operator's rooms in a hub: list, create, switch, admit and remove members,
|
|
1
|
+
// rooms: the operator's rooms in a hub: list, create, switch, admit and remove members, delete, move to a
|
|
2
2
|
// newer contract, and accept a new baseline for a log after reviewing an alarm.
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
4
|
import { DEFAULT_PROFILE, parseProfile } from '../work/index.js';
|
|
@@ -22,13 +22,10 @@ const opens = (H, n) => {
|
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
|
-
export const rooms = (H
|
|
25
|
+
export const rooms = (H) => {
|
|
26
26
|
const cur = config().room;
|
|
27
|
-
const view = (n, R) => ({ name: n, current: n === cur
|
|
28
|
-
|
|
29
|
-
const live = roomNames(H).flatMap(n => { const R = opens(H, n); return R ? [view(n, R)] : []; });
|
|
30
|
-
const gone = archived ? roomNames(H, true).flatMap(n => { const R = opens(H, n); return R ? [view(n, R)] : []; }) : [];
|
|
31
|
-
return [...live, ...gone];
|
|
27
|
+
const view = (n, R) => ({ name: n, current: n === cur, members: Object.keys(R.members).length, profile: R.profile.name, genesis: R.genesis });
|
|
28
|
+
return roomNames(H).flatMap(n => { const R = opens(H, n); return R ? [view(n, R)] : []; });
|
|
32
29
|
};
|
|
33
30
|
// A room carries its profile from the start: later changes to the hub's profiles do not reach it.
|
|
34
31
|
// `quorum`: the room's work starts by itself once it has that many agents; until then a join waits.
|
|
@@ -57,16 +54,6 @@ export const use = (H, name) => {
|
|
|
57
54
|
setConfig({ hub: H.name, room: R.name });
|
|
58
55
|
return R;
|
|
59
56
|
};
|
|
60
|
-
export const archive = (H, R) => {
|
|
61
|
-
notSigned(H, 'archive');
|
|
62
|
-
if (R.archived)
|
|
63
|
-
fail(`${R.name} is already archived`);
|
|
64
|
-
const name = `${R.name}@${R.genesis.slice(0, 8)}`;
|
|
65
|
-
locked(`hub-${H.name}`, () => H.store.archiveRoom(R.s, name));
|
|
66
|
-
if (config().room === R.name)
|
|
67
|
-
setConfig({ room: null });
|
|
68
|
-
return name;
|
|
69
|
-
};
|
|
70
57
|
// The room and everything kept for it: in the store (deleteRoom) and its readers' state. The sessions bound to it,
|
|
71
58
|
// on this machine or another, leave it on their own when they find its branch gone (core/agents/sessions).
|
|
72
59
|
export const erase = (H, R) => {
|
|
@@ -5,7 +5,7 @@ import { OPERATOR, controlChange } from '../rooms/index.js';
|
|
|
5
5
|
import { fail } from '../../../lib/index.js';
|
|
6
6
|
import { AGENT_RE, ID_RE } from '../protocol/index.js';
|
|
7
7
|
import { loadState, readerLock, saveState } from '../history/index.js';
|
|
8
|
-
// Removing
|
|
8
|
+
// Removing or deleting in a signed hub needs revocation published to every replica: not in this version.
|
|
9
9
|
export const notSigned = (H, what) => { if (H.signed)
|
|
10
10
|
fail(`room ${what} is not supported on signed hubs yet: a local change would come back with the next fetch`); };
|
|
11
11
|
export const admit = (H, R, who, key) => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// send: a message from the actor, its recipients, trailers and files checked before it is written.
|
|
2
|
-
import * as D from '../decisions/index.js';
|
|
3
2
|
import { OPERATOR, append } from '../rooms/index.js';
|
|
4
|
-
import { fail, note
|
|
3
|
+
import { fail, note } from '../../../lib/index.js';
|
|
5
4
|
import { ID_RE, TOKEN_RE, checkOutgoing, compose, lookup } from '../protocol/index.js';
|
|
6
5
|
import { linked, readerLock } from '../history/index.js';
|
|
7
6
|
import { isRef } from './actor.js';
|
|
@@ -44,14 +43,6 @@ const checkTrailers = (P, x, to, assign, re) => {
|
|
|
44
43
|
if (x.roles && !x.roles.split(',').every(role))
|
|
45
44
|
fail('--roles takes "<id>=<label>, …"; labels mean what the room profile says');
|
|
46
45
|
};
|
|
47
|
-
// The grant a piece of work spends: an approved decision with a budget, not revoked.
|
|
48
|
-
const grantOf = (R, all, ref) => {
|
|
49
|
-
if (!ref)
|
|
50
|
-
return null;
|
|
51
|
-
const d = lookup(all, ref);
|
|
52
|
-
const g = D.grantOf(all, R.authorityAt, d.id) ?? fail(`${short(d.id)} is not an approved decision with a budget`);
|
|
53
|
-
return g.revoked ? fail(`grant ${short(g.id)} was revoked`) : g.id;
|
|
54
|
-
};
|
|
55
46
|
// A message from the actor. The operator may omit `to`: the room, or the assignees when there are any.
|
|
56
47
|
export const send = ({ R, me }, x) => {
|
|
57
48
|
const P = R.profile;
|
|
@@ -60,12 +51,11 @@ export const send = ({ R, me }, x) => {
|
|
|
60
51
|
const assign = x.assign ? [...new Set(x.assign)] : null;
|
|
61
52
|
const to = recipients(me, x, re, assign);
|
|
62
53
|
checkTrailers(P, x, to, assign, re);
|
|
63
|
-
const grant = grantOf(R, all, x.grant);
|
|
64
54
|
const stagedNow = staged({ R, me });
|
|
65
55
|
const entries = outgoing(stagedNow, x.files ?? []);
|
|
66
56
|
const text = compose(x.text, [
|
|
67
57
|
['To', to.join(', ')], ['Re', re?.id], ['Kind', x.task && P.opens], ['State', x.state], ['Assign', assign?.join(', ')],
|
|
68
|
-
['Roles', x.roles], ['
|
|
58
|
+
['Roles', x.roles], ['Hop', re && String(re.hopEff + 1)],
|
|
69
59
|
]);
|
|
70
60
|
checkOutgoing(text, isRef(R));
|
|
71
61
|
const id = append(R, me, text, entries);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// view: how messages
|
|
1
|
+
// view: how messages and work are shown: as text framed for people and models, and as the JSON
|
|
2
2
|
// shapes every interface (the CLI's --json, a control panel) returns.
|
|
3
3
|
import { bytes, when } from '../../../lib/index.js';
|
|
4
4
|
import { KNOWN } from '../protocol/index.js';
|
|
@@ -44,9 +44,5 @@ export const asJson = (m) => ({ id: m.id, from: m.from, trust: m.trust, to: m.to
|
|
|
44
44
|
assign: m.assign, roles: m.roles, hop: m.hop, hopEff: m.hopEff, bad: m.bad, time: new Date(m.time * 1000).toISOString(), subject: m.subject,
|
|
45
45
|
body: m.body, files: m.files.map(f => ({ path: f.path, size: f.size })), refused: m.refused, trailers: m.trailers });
|
|
46
46
|
export const taskView = (t, taken) => ({ id: t.root.id, subject: t.root.subject, requester: t.requester,
|
|
47
|
-
assignees: t.assignees, roles: t.roles, taken,
|
|
48
|
-
export const proposalView = (x) => ({ id: x.proposal.id, subject: x.proposal.subject, from: x.proposal.from,
|
|
49
|
-
decider: x.decider, state: x.state, budget: x.budget, stamps: x.stamps, missing: x.missing });
|
|
50
|
-
export const grantView = (x, usage) => ({ grant: x.grant.id, proposal: x.proposal.id,
|
|
51
|
-
subject: x.proposal.subject, budget: x.grant.budget, revoked: x.grant.revoked, usage });
|
|
47
|
+
assignees: t.assignees, roles: t.roles, taken, state: t.state, by: t.by, at: new Date(t.at * 1000).toISOString() });
|
|
52
48
|
//# sourceMappingURL=view.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// task: a piece of work folded over its messages in thread order, by the room's profile.
|
|
2
|
-
import {
|
|
2
|
+
import { ordered } from '../protocol/index.js';
|
|
3
3
|
// Who a principal of a rule is in a piece of work.
|
|
4
4
|
const PRINCIPALS = {
|
|
5
5
|
requester: (who, t) => who === t.requester,
|
|
@@ -16,12 +16,6 @@ export const taskFor = (all, m, profile) => {
|
|
|
16
16
|
return x;
|
|
17
17
|
return null;
|
|
18
18
|
};
|
|
19
|
-
const grant = (profile, t, b, m) => {
|
|
20
|
-
if (!one(m, 'grant') || !allows(profile.rules.grant, m.from, b))
|
|
21
|
-
return null;
|
|
22
|
-
t.grant = one(m, 'grant');
|
|
23
|
-
return 'grant';
|
|
24
|
-
};
|
|
25
19
|
const roles = (profile, t, b, m) => {
|
|
26
20
|
if (!m.roles || !allows(profile.rules.roles, m.from, b))
|
|
27
21
|
return null;
|
|
@@ -43,12 +37,11 @@ const state = (profile, t, b, m) => {
|
|
|
43
37
|
Object.assign(t, { state: m.state, by: m.from, at: m.time });
|
|
44
38
|
return `state.${m.state}`;
|
|
45
39
|
};
|
|
46
|
-
const STEPS = [
|
|
40
|
+
const STEPS = [roles, assign, state];
|
|
47
41
|
const opening = (root, profile, msgs) => ({ root, msgs, state: profile.initial, by: root.from, at: root.time,
|
|
48
42
|
requester: root.from, roles: { ...root.roles }, assignees: root.assign ?? (root.to.includes('*') ? [] : root.to.filter(x => x !== root.from)),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const partOf = (all, m, root, profile) => !m.bad && !(m.kind && HEARTBEATS.has(m.kind)) && taskFor(all, m, profile) === root;
|
|
43
|
+
effects: new Map([[root.id, ['opened', ...(root.assign ? ['assigned'] : [])]]]) });
|
|
44
|
+
const partOf = (all, m, root, profile) => !m.bad && taskFor(all, m, profile) === root;
|
|
52
45
|
// A work item folds over its messages in thread order. Every right is judged on the work as it was before the
|
|
53
46
|
// message, so a label given in a message authorizes nothing in that same message, while its holder may still hand it
|
|
54
47
|
// over and reassign in one reply. Opening sets the initial state and assignees (Assign, else To minus the opener; none
|
package/target/esm/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
// The library: the message protocol, profiles,
|
|
1
|
+
// The library: the message protocol, profiles, signals and the Store port, for programs that coordinate
|
|
2
2
|
// through ringi without its command line (a framework's own runner, another store, another client).
|
|
3
3
|
// Importing it wires this machine's defaults (composition/); a program that brings its own adapters calls wire().
|
|
4
4
|
import './composition/index.js';
|
|
5
5
|
export * from './core/hub/protocol/index.js';
|
|
6
6
|
export * from './core/hub/work/index.js';
|
|
7
|
-
export * as decide from './core/hub/decisions/index.js';
|
|
8
7
|
export * as signals from './core/hub/signals/index.js';
|
|
9
8
|
export { openHub, openRoom, createHub, contractAt } from './core/hub/rooms/index.js';
|
|
10
9
|
//# sourceMappingURL=index.js.map
|
package/target/esm/lib/json.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// json: JSON read strictly. JSON.parse silently keeps the last of duplicate
|
|
2
|
-
// fields; for contracts and
|
|
2
|
+
// fields; for contracts and profiles that hides a second value, so the structure is checked first and a duplicate
|
|
3
3
|
// field is refused.
|
|
4
4
|
import { fail } from './fail.js';
|
|
5
5
|
const LITERAL = /^(?:true|false|null|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)/;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export type GroupMember = {
|
|
2
|
-
group: number;
|
|
3
|
-
state: string;
|
|
4
|
-
};
|
|
5
|
-
export declare const procMember: (text: string) => GroupMember | null;
|
|
6
|
-
export declare const psMember: (text: string) => GroupMember | null;
|
|
7
|
-
export declare const groupRunning: (group: number, members: Array<GroupMember | null>) => boolean | null;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const groupAlive: (group: number) => boolean;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { Ancestor, ProcessRows } from '../../core/agents/ports/host.ts';
|
|
2
|
-
export declare const supported: boolean;
|
|
3
|
-
export declare const alive: (pid: number) => boolean;
|
|
4
|
-
export declare const snapshot: () => ProcessRows | null;
|
|
5
|
-
export declare const ancestors: (first: number) => Ancestor[];
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare const seconds: (time: string) => number;
|
|
2
|
-
export declare const procStat: (line: string) => {
|
|
3
|
-
ppid: number;
|
|
4
|
-
cpu: number;
|
|
5
|
-
} | null;
|
|
6
|
-
export declare const treeCpu: (pid: number, rows: Map<number, {
|
|
7
|
-
ppid: number;
|
|
8
|
-
cpu: number;
|
|
9
|
-
}> | null) => number | null;
|