edge-book 0.15.0 → 0.16.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/dist/edge-book.js +244 -16
- package/package.json +3 -1
package/dist/edge-book.js
CHANGED
|
@@ -935,6 +935,7 @@ async function canReadObject(store, objectId, subjectAgentId, at = Date.now()) {
|
|
|
935
935
|
for (const grant of candidates) {
|
|
936
936
|
if (await store.verifyGrantSignature(grant)) return true;
|
|
937
937
|
}
|
|
938
|
+
await store.audit("object.read.denied", subjectAgentId, { object_id: objectId, scope: "object.read" });
|
|
938
939
|
return false;
|
|
939
940
|
}
|
|
940
941
|
async function readObject(store, objectId, subjectAgentId) {
|
|
@@ -974,6 +975,7 @@ async function shareObjectEnvelope(store, peerAgentId, objectId, expiresAt = "")
|
|
|
974
975
|
if (object.attachment) {
|
|
975
976
|
attachment_b64 = (await fs5.readFile(store.file(object.attachment.ref))).toString("base64");
|
|
976
977
|
}
|
|
978
|
+
await store.audit("object.share", peerAgentId, { object_id: objectId, grant_id: grant.grant_id, scope: "object.read" });
|
|
977
979
|
return store.signEnvelope({
|
|
978
980
|
type: "object_share",
|
|
979
981
|
to_agent_id: peerAgentId,
|
|
@@ -1019,7 +1021,7 @@ async function revokeObjectGrant(store, objectId, subjectAgentId) {
|
|
|
1019
1021
|
}
|
|
1020
1022
|
if (revoked.length) {
|
|
1021
1023
|
await store.saveGrants(grants);
|
|
1022
|
-
await store.audit("grant.revoke", subjectAgentId, { object_id: objectId, grant_ids: revoked });
|
|
1024
|
+
await store.audit("grant.revoke", subjectAgentId, { object_id: objectId, grant_ids: revoked, scope: "object.read" });
|
|
1023
1025
|
}
|
|
1024
1026
|
return revoked;
|
|
1025
1027
|
}
|
|
@@ -1823,13 +1825,18 @@ async function broadcastProfileEnvelopes(store) {
|
|
|
1823
1825
|
async function revoke(store, peerAgentId) {
|
|
1824
1826
|
await store.setRelationship(peerAgentId, "revoked", "Revoke", "revoked");
|
|
1825
1827
|
const grants = await store.grants();
|
|
1828
|
+
const revoked = [];
|
|
1829
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
1826
1830
|
for (const grant of Object.values(grants)) {
|
|
1827
1831
|
if (grant.subject_agent_id === peerAgentId || grant.issuer_agent_id === peerAgentId) {
|
|
1828
1832
|
grant.status = "revoked";
|
|
1829
1833
|
grant.revoked_at = now2();
|
|
1834
|
+
revoked.push(grant.grant_id);
|
|
1835
|
+
for (const scope of grant.scopes) scopes.add(scope);
|
|
1830
1836
|
}
|
|
1831
1837
|
}
|
|
1832
1838
|
await store.saveGrants(grants);
|
|
1839
|
+
if (revoked.length) await store.audit("grant.revoke", peerAgentId, { grant_ids: revoked, scopes: [...scopes].sort() });
|
|
1833
1840
|
}
|
|
1834
1841
|
async function block(store, peerAgentId) {
|
|
1835
1842
|
await store.setRelationship(peerAgentId, "blocked", "Block", "blocked");
|
|
@@ -2021,6 +2028,8 @@ async function updateConfig(store, input) {
|
|
|
2021
2028
|
if (input.inbound_max_global !== void 0) next.inbound_max_global = input.inbound_max_global;
|
|
2022
2029
|
if (input.inbound_window_ms !== void 0) next.inbound_window_ms = input.inbound_window_ms;
|
|
2023
2030
|
if (input.handle_nudge_at !== void 0) next.handle_nudge_at = input.handle_nudge_at;
|
|
2031
|
+
if (input.onboarding_nudge_at !== void 0) next.onboarding_nudge_at = input.onboarding_nudge_at;
|
|
2032
|
+
if (input.onboarding_nudge_count !== void 0) next.onboarding_nudge_count = input.onboarding_nudge_count;
|
|
2024
2033
|
if (input.greeter_mode !== void 0) next.greeter_mode = input.greeter_mode;
|
|
2025
2034
|
if (input.support_inbox !== void 0) next.support_inbox = input.support_inbox;
|
|
2026
2035
|
if (input.greeter_welcome_object_id !== void 0) next.greeter_welcome_object_id = input.greeter_welcome_object_id;
|
|
@@ -2505,15 +2514,36 @@ async function receiveEnvelope(store, envelope) {
|
|
|
2505
2514
|
}
|
|
2506
2515
|
async function audit(store, action, peerAgentId, details) {
|
|
2507
2516
|
const audit_id = randomId("audit");
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2517
|
+
try {
|
|
2518
|
+
const identity = await store.identity();
|
|
2519
|
+
await appendJsonl(store.file(AUDIT_FILE), {
|
|
2520
|
+
audit_id,
|
|
2521
|
+
created_at: now2(),
|
|
2522
|
+
kind: action,
|
|
2523
|
+
action,
|
|
2524
|
+
actor_agent_id: identity.agent_id,
|
|
2525
|
+
peer_agent_id: peerAgentId,
|
|
2526
|
+
...auditIndexFields(details),
|
|
2527
|
+
details
|
|
2528
|
+
});
|
|
2529
|
+
} catch {
|
|
2530
|
+
}
|
|
2515
2531
|
return audit_id;
|
|
2516
2532
|
}
|
|
2533
|
+
function auditIndexFields(details) {
|
|
2534
|
+
const fields = {};
|
|
2535
|
+
const objectId = details.object_id;
|
|
2536
|
+
const grantId = details.grant_id;
|
|
2537
|
+
const grantIds = details.grant_ids;
|
|
2538
|
+
const scope = details.scope;
|
|
2539
|
+
const scopes = details.scopes;
|
|
2540
|
+
if (typeof objectId === "string") fields.object_id = objectId;
|
|
2541
|
+
if (typeof grantId === "string") fields.grant_id = grantId;
|
|
2542
|
+
if (Array.isArray(grantIds) && grantIds.every((id) => typeof id === "string")) fields.grant_ids = grantIds.join(",");
|
|
2543
|
+
if (typeof scope === "string") fields.grant_scope = scope;
|
|
2544
|
+
if (Array.isArray(scopes) && scopes.every((s) => typeof s === "string")) fields.grant_scope = scopes.join(",");
|
|
2545
|
+
return fields;
|
|
2546
|
+
}
|
|
2517
2547
|
|
|
2518
2548
|
// src/store-notify.ts
|
|
2519
2549
|
async function peerName(store, agentId) {
|
|
@@ -4972,6 +5002,114 @@ var PairCompleteWaiter = class {
|
|
|
4972
5002
|
}
|
|
4973
5003
|
};
|
|
4974
5004
|
|
|
5005
|
+
// src/wire-schema.ts
|
|
5006
|
+
var WIRE_FRAMES_SCHEMA = { "$id": "edge-book/wire-frames", "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "HandleClaimErrFrame": { "properties": { "reason": { "enum": ["taken", "bad_sig", "bad_format", "bad_card"], "type": "string" }, "request_id": { "type": "string" }, "type": { "const": "handle_claim_err", "type": "string" } }, "required": ["type", "request_id", "reason"], "type": "object" }, "HandleClaimFrame": { "properties": { "card": {}, "claim_sig": { "type": "string" }, "claimed_at": { "type": "number" }, "discoverable": { "type": "boolean" }, "handle": { "type": "string" }, "request_id": { "type": "string" }, "type": { "const": "handle_claim", "type": "string" } }, "required": ["type", "request_id", "handle", "card", "claimed_at", "claim_sig"], "type": "object" }, "HandleClaimOkFrame": { "properties": { "handle": { "type": "string" }, "request_id": { "type": "string" }, "type": { "const": "handle_claim_ok", "type": "string" } }, "required": ["type", "request_id", "handle"], "type": "object" }, "MailboxAckFrame": { "properties": { "id": { "type": "string" }, "type": { "const": "mailbox_ack", "type": "string" } }, "required": ["type", "id"], "type": "object" }, "MailboxDeliverFrame": { "properties": { "blob_b64": { "type": "string" }, "from": { "$ref": "#/definitions/RecipientAddress" }, "id": { "type": "string" }, "trace_id": { "type": "string" }, "ts": { "type": "number" }, "type": { "const": "mailbox_deliver", "type": "string" } }, "required": ["type", "id", "from", "blob_b64", "ts"], "type": "object" }, "MailboxMessage": { "properties": { "blob": { "type": "string" }, "from": { "$ref": "#/definitions/RecipientAddress" }, "id": { "type": "string" }, "to": { "$ref": "#/definitions/RecipientAddress" }, "trace_id": { "type": "string" }, "ts": { "type": "number" } }, "required": ["id", "to", "from", "blob", "ts"], "type": "object" }, "MailboxSendErrFrame": { "properties": { "error": { "type": "string" }, "request_id": { "type": "string" }, "type": { "const": "mailbox_send_err", "type": "string" } }, "required": ["type", "request_id", "error"], "type": "object" }, "MailboxSendFrame": { "properties": { "blob_b64": { "type": "string" }, "request_id": { "type": "string" }, "to": { "$ref": "#/definitions/RecipientAddress" }, "trace_id": { "type": "string" }, "type": { "const": "mailbox_send", "type": "string" } }, "required": ["type", "request_id", "to", "blob_b64"], "type": "object" }, "MailboxSendOkFrame": { "properties": { "id": { "type": "string" }, "recipient_live": { "type": "boolean" }, "request_id": { "type": "string" }, "type": { "const": "mailbox_send_ok", "type": "string" } }, "required": ["type", "request_id", "id"], "type": "object" }, "MailboxStatusEntry": { "properties": { "id": { "type": "string" }, "queued_ms": { "type": "number" }, "recipient_live": { "type": "boolean" }, "state": { "enum": ["queued", "delivered", "acked", "unknown"], "type": "string" } }, "required": ["id", "state"], "type": "object" }, "MailboxStatusErrFrame": { "properties": { "error": { "type": "string" }, "request_id": { "type": "string" }, "type": { "const": "mailbox_status_err", "type": "string" } }, "required": ["type", "request_id", "error"], "type": "object" }, "MailboxStatusFrame": { "properties": { "ids": { "items": { "type": "string" }, "type": "array" }, "request_id": { "type": "string" }, "type": { "const": "mailbox_status", "type": "string" } }, "required": ["type", "request_id", "ids"], "type": "object" }, "MailboxStatusOkFrame": { "properties": { "request_id": { "type": "string" }, "statuses": { "items": { "$ref": "#/definitions/MailboxStatusEntry" }, "type": "array" }, "type": { "const": "mailbox_status_ok", "type": "string" } }, "required": ["type", "request_id", "statuses"], "type": "object" }, "RecipientAddress": { "type": "string" }, "WireFrame": { "anyOf": [{ "$ref": "#/definitions/MailboxSendFrame" }, { "$ref": "#/definitions/MailboxSendOkFrame" }, { "$ref": "#/definitions/MailboxSendErrFrame" }, { "$ref": "#/definitions/MailboxDeliverFrame" }, { "$ref": "#/definitions/MailboxAckFrame" }, { "$ref": "#/definitions/MailboxStatusFrame" }, { "$ref": "#/definitions/MailboxStatusOkFrame" }, { "$ref": "#/definitions/MailboxStatusErrFrame" }, { "$ref": "#/definitions/HandleClaimFrame" }, { "$ref": "#/definitions/HandleClaimOkFrame" }, { "$ref": "#/definitions/HandleClaimErrFrame" }] } }, "description": "Host<->agent wire-frame contract, generated from src/contracts.ts (edge-book-host). Regenerate with `npm run schemas`; do not edit." };
|
|
5007
|
+
|
|
5008
|
+
// src/frame-validate.ts
|
|
5009
|
+
var DEFINITIONS = WIRE_FRAMES_SCHEMA.definitions;
|
|
5010
|
+
var MAX_ERRORS = 5;
|
|
5011
|
+
function validateWireFrame(defName, value) {
|
|
5012
|
+
const def = Object.prototype.hasOwnProperty.call(DEFINITIONS, defName) ? DEFINITIONS[defName] : void 0;
|
|
5013
|
+
if (!def) return { ok: false, errors: [`unknown schema definition: ${defName}`] };
|
|
5014
|
+
const errors = [];
|
|
5015
|
+
check(def, value, "$", errors);
|
|
5016
|
+
return errors.length ? { ok: false, errors } : { ok: true };
|
|
5017
|
+
}
|
|
5018
|
+
function typeOf(value) {
|
|
5019
|
+
if (value === null) return "null";
|
|
5020
|
+
if (Array.isArray(value)) return "array";
|
|
5021
|
+
return typeof value;
|
|
5022
|
+
}
|
|
5023
|
+
function check(schema, value, at, errors) {
|
|
5024
|
+
if (errors.length >= MAX_ERRORS) return;
|
|
5025
|
+
const ref = schema.$ref;
|
|
5026
|
+
if (typeof ref === "string") {
|
|
5027
|
+
checkRef(ref, value, at, errors);
|
|
5028
|
+
return;
|
|
5029
|
+
}
|
|
5030
|
+
const anyOf = schema.anyOf;
|
|
5031
|
+
if (Array.isArray(anyOf)) {
|
|
5032
|
+
const matched = anyOf.some((branch) => {
|
|
5033
|
+
const branchErrors = [];
|
|
5034
|
+
check(branch, value, at, branchErrors);
|
|
5035
|
+
return branchErrors.length === 0;
|
|
5036
|
+
});
|
|
5037
|
+
if (!matched) errors.push(`${at}: matched no anyOf branch`);
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
5040
|
+
if ("const" in schema && value !== schema.const) {
|
|
5041
|
+
errors.push(`${at}: expected const ${JSON.stringify(schema.const)}, got ${JSON.stringify(value)}`);
|
|
5042
|
+
return;
|
|
5043
|
+
}
|
|
5044
|
+
const allowed = schema.enum;
|
|
5045
|
+
if (Array.isArray(allowed) && !allowed.some((v) => v === value)) {
|
|
5046
|
+
errors.push(`${at}: ${JSON.stringify(value)} not in enum ${JSON.stringify(allowed)}`);
|
|
5047
|
+
return;
|
|
5048
|
+
}
|
|
5049
|
+
const type = schema.type;
|
|
5050
|
+
if (typeof type === "string") {
|
|
5051
|
+
const actual = typeOf(value);
|
|
5052
|
+
if (type === "object" ? actual !== "object" : actual !== type) {
|
|
5053
|
+
errors.push(`${at}: expected ${type}, got ${actual}`);
|
|
5054
|
+
return;
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
if (type === "object") checkObject(schema, value, at, errors);
|
|
5058
|
+
if (type === "array" && Array.isArray(value)) checkArray(schema, value, at, errors);
|
|
5059
|
+
}
|
|
5060
|
+
function checkRef(ref, value, at, errors) {
|
|
5061
|
+
const name = ref.replace("#/definitions/", "");
|
|
5062
|
+
const target = Object.prototype.hasOwnProperty.call(DEFINITIONS, name) ? DEFINITIONS[name] : void 0;
|
|
5063
|
+
if (!target) {
|
|
5064
|
+
errors.push(`${at}: unresolvable $ref ${ref}`);
|
|
5065
|
+
return;
|
|
5066
|
+
}
|
|
5067
|
+
check(target, value, at, errors);
|
|
5068
|
+
}
|
|
5069
|
+
function checkObject(schema, obj, at, errors) {
|
|
5070
|
+
const required = schema.required;
|
|
5071
|
+
if (Array.isArray(required)) {
|
|
5072
|
+
for (const key of required) {
|
|
5073
|
+
if (errors.length >= MAX_ERRORS) return;
|
|
5074
|
+
if (!(typeof key === "string" && Object.prototype.hasOwnProperty.call(obj, key))) errors.push(`${at}: missing required property "${String(key)}"`);
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
const properties = schema.properties;
|
|
5078
|
+
if (properties && typeof properties === "object") {
|
|
5079
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
5080
|
+
if (errors.length >= MAX_ERRORS) return;
|
|
5081
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) check(propSchema, obj[key], `${at}.${key}`, errors);
|
|
5082
|
+
}
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
function checkArray(schema, value, at, errors) {
|
|
5086
|
+
const items = schema.items;
|
|
5087
|
+
if (items && typeof items === "object" && !Array.isArray(items)) {
|
|
5088
|
+
for (let i = 0; i < value.length; i++) {
|
|
5089
|
+
if (errors.length >= MAX_ERRORS) return;
|
|
5090
|
+
check(items, value[i], `${at}[${i}]`, errors);
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
var INBOUND_GATES = {
|
|
5095
|
+
mailbox_deliver: "MailboxDeliverFrame",
|
|
5096
|
+
mailbox_send_ok: "MailboxSendOkFrame",
|
|
5097
|
+
mailbox_send_err: "MailboxSendErrFrame",
|
|
5098
|
+
mailbox_status_ok: "MailboxStatusOkFrame",
|
|
5099
|
+
mailbox_status_err: "MailboxStatusErrFrame",
|
|
5100
|
+
handle_claim_ok: "HandleClaimOkFrame",
|
|
5101
|
+
handle_claim_err: "HandleClaimErrFrame"
|
|
5102
|
+
};
|
|
5103
|
+
function gateHostFrame(frame) {
|
|
5104
|
+
const type = frame && typeof frame === "object" ? frame.type : void 0;
|
|
5105
|
+
const def = typeof type === "string" && Object.prototype.hasOwnProperty.call(INBOUND_GATES, type) ? INBOUND_GATES[type] : void 0;
|
|
5106
|
+
if (!def) return { ok: true };
|
|
5107
|
+
const result = validateWireFrame(def, frame);
|
|
5108
|
+
if (result.ok) return { ok: true };
|
|
5109
|
+
const paths = result.errors.map((e) => e.split(":")[0] ?? e).join(",");
|
|
5110
|
+
return { ok: false, frameType: type, errorPaths: paths };
|
|
5111
|
+
}
|
|
5112
|
+
|
|
4975
5113
|
// src/dialout-oneshot.ts
|
|
4976
5114
|
async function sendPairRegistration(options) {
|
|
4977
5115
|
const client = new EdgeBookDialoutClient({ ...options, reconnect: false, openLocalApi: false });
|
|
@@ -5290,6 +5428,11 @@ var EdgeBookDialoutClient = class {
|
|
|
5290
5428
|
async handleMessage(data) {
|
|
5291
5429
|
const text = typeof data === "string" ? data : Buffer.isBuffer(data) ? data.toString("utf8") : String(data);
|
|
5292
5430
|
const frame = JSON.parse(text);
|
|
5431
|
+
const gate = gateHostFrame(frame);
|
|
5432
|
+
if (!gate.ok) {
|
|
5433
|
+
await logEvent(this.store, "frame.invalid", { frame_type: gate.frameType, errors: gate.errorPaths });
|
|
5434
|
+
return;
|
|
5435
|
+
}
|
|
5293
5436
|
if (frame.type === "hello_ok") {
|
|
5294
5437
|
this.opened?.resolve();
|
|
5295
5438
|
this.opened = void 0;
|
|
@@ -5701,6 +5844,36 @@ ${buildHandleNudge(suggestion)}` };
|
|
|
5701
5844
|
}
|
|
5702
5845
|
}
|
|
5703
5846
|
|
|
5847
|
+
// src/onboarding-nudge.ts
|
|
5848
|
+
var ONBOARDING_NUDGE_THROTTLE_MS = 6 * 60 * 60 * 1e3;
|
|
5849
|
+
var ONBOARDING_NUDGE_MAX_EMITS = 3;
|
|
5850
|
+
function buildOnboardingNudgeText() {
|
|
5851
|
+
return [
|
|
5852
|
+
`---`,
|
|
5853
|
+
`Onboarding incomplete: this agent has no friends and no shares yet \u2014 the human was likely never onboarded.`,
|
|
5854
|
+
`Load skills/edge-book/prompts/onboard.md and run the first-session script with your human now.`
|
|
5855
|
+
].join("\n");
|
|
5856
|
+
}
|
|
5857
|
+
async function maybeAppendOnboardingNudge(store, command, result, now4 = Date.now()) {
|
|
5858
|
+
if (!NUDGE_COMMANDS.has(command)) return result;
|
|
5859
|
+
try {
|
|
5860
|
+
await store.identity();
|
|
5861
|
+
const config = await store.config();
|
|
5862
|
+
const count = config.onboarding_nudge_count ?? 0;
|
|
5863
|
+
if (count >= ONBOARDING_NUDGE_MAX_EMITS) return result;
|
|
5864
|
+
if (config.onboarding_nudge_at !== void 0 && now4 - config.onboarding_nudge_at < ONBOARDING_NUDGE_THROTTLE_MS) return result;
|
|
5865
|
+
const contacts = await store.contacts();
|
|
5866
|
+
if (Object.keys(contacts).length > 0) return result;
|
|
5867
|
+
const objects2 = await store.objects();
|
|
5868
|
+
if (Object.keys(objects2).length > 0) return result;
|
|
5869
|
+
await store.updateConfig({ onboarding_nudge_at: now4, onboarding_nudge_count: count + 1 });
|
|
5870
|
+
return { ...result, text: `${result.text}
|
|
5871
|
+
${buildOnboardingNudgeText()}` };
|
|
5872
|
+
} catch {
|
|
5873
|
+
return result;
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
|
|
5704
5877
|
// src/cli-identity.ts
|
|
5705
5878
|
import fs11 from "fs/promises";
|
|
5706
5879
|
import path11 from "path";
|
|
@@ -5819,6 +5992,7 @@ function ensureGreeterCron(opts) {
|
|
|
5819
5992
|
// src/doctor.ts
|
|
5820
5993
|
var DOCTOR_EVENT_TAIL = 50;
|
|
5821
5994
|
var DOCTOR_TRACE_TAIL = 10;
|
|
5995
|
+
var DOCTOR_AUDIT_TAIL = 20;
|
|
5822
5996
|
var DEFAULT_RELAY_TIMEOUT_MS = 3e3;
|
|
5823
5997
|
function recentTraces(events, limit = DOCTOR_TRACE_TAIL) {
|
|
5824
5998
|
const out = [];
|
|
@@ -5857,6 +6031,42 @@ function notifierCronState(runner) {
|
|
|
5857
6031
|
return "error";
|
|
5858
6032
|
}
|
|
5859
6033
|
}
|
|
6034
|
+
function stringField(value) {
|
|
6035
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6036
|
+
}
|
|
6037
|
+
function detailString(details, key) {
|
|
6038
|
+
if (!details || typeof details !== "object") return void 0;
|
|
6039
|
+
const value = details[key];
|
|
6040
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
6041
|
+
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) return value.join(",");
|
|
6042
|
+
return void 0;
|
|
6043
|
+
}
|
|
6044
|
+
function firstString(...values) {
|
|
6045
|
+
for (const value of values) {
|
|
6046
|
+
const found = stringField(value);
|
|
6047
|
+
if (found) return found;
|
|
6048
|
+
}
|
|
6049
|
+
return void 0;
|
|
6050
|
+
}
|
|
6051
|
+
function addAuditField(event, key, value) {
|
|
6052
|
+
if (value) event[key] = value;
|
|
6053
|
+
}
|
|
6054
|
+
function auditEvent(event) {
|
|
6055
|
+
const out = {
|
|
6056
|
+
ts: firstString(event.created_at, event.ts) ?? "",
|
|
6057
|
+
kind: firstString(event.kind, event.action) ?? "unknown"
|
|
6058
|
+
};
|
|
6059
|
+
addAuditField(out, "actor_agent_id", stringField(event.actor_agent_id));
|
|
6060
|
+
addAuditField(out, "peer_agent_id", stringField(event.peer_agent_id));
|
|
6061
|
+
addAuditField(out, "object_id", firstString(event.object_id, detailString(event.details, "object_id")));
|
|
6062
|
+
addAuditField(out, "grant_id", firstString(event.grant_id, detailString(event.details, "grant_id")));
|
|
6063
|
+
addAuditField(out, "grant_ids", firstString(event.grant_ids, detailString(event.details, "grant_ids")));
|
|
6064
|
+
addAuditField(out, "grant_scope", firstString(event.grant_scope, detailString(event.details, "scope"), detailString(event.details, "scopes")));
|
|
6065
|
+
return out;
|
|
6066
|
+
}
|
|
6067
|
+
function auditTail(events, limit = DOCTOR_AUDIT_TAIL) {
|
|
6068
|
+
return events.slice(-limit).map(auditEvent);
|
|
6069
|
+
}
|
|
5860
6070
|
async function buildDoctorReport(store, opts) {
|
|
5861
6071
|
const legacy = await store.doctor();
|
|
5862
6072
|
const config = await store.config();
|
|
@@ -5917,6 +6127,7 @@ async function buildDoctorReport(store, opts) {
|
|
|
5917
6127
|
},
|
|
5918
6128
|
stores,
|
|
5919
6129
|
events: await readEvents(store, DOCTOR_EVENT_TAIL),
|
|
6130
|
+
audit: auditTail(await store.auditEvents()),
|
|
5920
6131
|
// Traces are derived from the FULL event log (not just the tail) so a
|
|
5921
6132
|
// busy log does not hide an older still-relevant trace.
|
|
5922
6133
|
traces: recentTraces(await readEvents(store))
|
|
@@ -5926,6 +6137,25 @@ function renderEventLine(e) {
|
|
|
5926
6137
|
const extras = Object.entries(e).filter(([k]) => k !== "ts" && k !== "kind").map(([k, v]) => `${k}=${String(v)}`).join(" ");
|
|
5927
6138
|
return ` ${e.ts} ${e.kind}${extras ? ` ${extras}` : ""}`;
|
|
5928
6139
|
}
|
|
6140
|
+
function renderAuditLine(e) {
|
|
6141
|
+
const extras = Object.entries(e).filter(([k]) => k !== "ts" && k !== "kind").map(([k, v]) => `${k}=${v}`).join(" ");
|
|
6142
|
+
return ` ${e.ts} ${e.kind}${extras ? ` ${extras}` : ""}`;
|
|
6143
|
+
}
|
|
6144
|
+
function appendTraceSection(lines, traces) {
|
|
6145
|
+
lines.push(`Recent traces (distinct trace_ids, newest last, up to ${DOCTOR_TRACE_TAIL})`);
|
|
6146
|
+
if (traces.length === 0) lines.push(" (no traced envelopes yet)");
|
|
6147
|
+
for (const t of traces) lines.push(` ${t.ts} ${t.direction === "out" ? "\u2192" : "\u2190"} ${t.kind} ${t.trace_id}`);
|
|
6148
|
+
}
|
|
6149
|
+
function appendEventSection(lines, events) {
|
|
6150
|
+
lines.push(`Event log (newest last, up to ${DOCTOR_EVENT_TAIL})`);
|
|
6151
|
+
if (events.length === 0) lines.push(" (no events recorded yet)");
|
|
6152
|
+
for (const e of events) lines.push(renderEventLine(e));
|
|
6153
|
+
}
|
|
6154
|
+
function appendAuditSection(lines, audit2) {
|
|
6155
|
+
lines.push(`Audit log (newest last, up to ${DOCTOR_AUDIT_TAIL})`);
|
|
6156
|
+
if (audit2.length === 0) lines.push(" (no audit records yet)");
|
|
6157
|
+
for (const e of audit2) lines.push(renderAuditLine(e));
|
|
6158
|
+
}
|
|
5929
6159
|
function renderDoctorText(report) {
|
|
5930
6160
|
const lines = [];
|
|
5931
6161
|
lines.push(`Edge Book doctor \u2014 v${report.version} (${report.generated_at})`);
|
|
@@ -5963,13 +6193,11 @@ function renderDoctorText(report) {
|
|
|
5963
6193
|
lines.push(` contacts: ${report.stores.contacts} (friends: ${report.stores.friends})`);
|
|
5964
6194
|
lines.push(` posts: ${report.stores.posts} objects: ${report.stores.objects} escalations: ${report.stores.escalations} pending approvals: ${report.stores.pending_approvals}`);
|
|
5965
6195
|
lines.push("");
|
|
5966
|
-
lines.
|
|
5967
|
-
if (report.traces.length === 0) lines.push(" (no traced envelopes yet)");
|
|
5968
|
-
for (const t of report.traces) lines.push(` ${t.ts} ${t.direction === "out" ? "\u2192" : "\u2190"} ${t.kind} ${t.trace_id}`);
|
|
6196
|
+
appendTraceSection(lines, report.traces);
|
|
5969
6197
|
lines.push("");
|
|
5970
|
-
lines
|
|
5971
|
-
|
|
5972
|
-
|
|
6198
|
+
appendEventSection(lines, report.events);
|
|
6199
|
+
lines.push("");
|
|
6200
|
+
appendAuditSection(lines, report.audit);
|
|
5973
6201
|
return lines.join("\n");
|
|
5974
6202
|
}
|
|
5975
6203
|
|
|
@@ -7491,7 +7719,7 @@ async function handleCli(inputArgs, ctx = {}) {
|
|
|
7491
7719
|
const socialResult = await handleSocialCli(command, args, ctx, home, store);
|
|
7492
7720
|
if (socialResult) {
|
|
7493
7721
|
if (command === "friend" && socialAction === "auto-accept") return socialResult;
|
|
7494
|
-
return maybeAppendHandleNudge(store, command, socialResult);
|
|
7722
|
+
return maybeAppendOnboardingNudge(store, command, await maybeAppendHandleNudge(store, command, socialResult));
|
|
7495
7723
|
}
|
|
7496
7724
|
const supportResult = await handleSupportCli(command, args, ctx, home, store);
|
|
7497
7725
|
if (supportResult) return supportResult;
|
|
@@ -7673,7 +7901,7 @@ ${JSON.stringify(result, null, 2)}`, json: result };
|
|
|
7673
7901
|
}
|
|
7674
7902
|
}
|
|
7675
7903
|
const taxonomyResult = await handleTaxonomyCli(command, args, ctx, store);
|
|
7676
|
-
if (taxonomyResult) return maybeAppendHandleNudge(store, command, taxonomyResult);
|
|
7904
|
+
if (taxonomyResult) return maybeAppendOnboardingNudge(store, command, await maybeAppendHandleNudge(store, command, taxonomyResult));
|
|
7677
7905
|
const directoryResult = await handleDirectoryCli(command, args, ctx, home, store);
|
|
7678
7906
|
if (directoryResult) return directoryResult;
|
|
7679
7907
|
throw new EdgeBookError("unknown_command", usage());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edge-book",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Run your own Edge Book agent and connect it to the hosted reader.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"prepublishOnly": "npm run sync-readme:check && npm run build",
|
|
18
18
|
"sync-readme": "tsx scripts/sync-readme.ts",
|
|
19
19
|
"sync-readme:check": "tsx scripts/sync-readme.ts --check",
|
|
20
|
+
"schemas": "tsx scripts/sync-wire-schema.ts",
|
|
21
|
+
"schemas:check": "tsx scripts/sync-wire-schema.ts --check",
|
|
20
22
|
"smoke": "node scripts/smoke-2agent.ts",
|
|
21
23
|
"smoke:host": "node scripts/smoke-2agent.ts --host",
|
|
22
24
|
"lint": "eslint src"
|