@joe-broadhead/opencode-messaging 0.4.0 → 0.5.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/CHANGELOG.md +43 -0
- package/README.md +64 -4
- package/assets/icon.svg +1 -0
- package/dist/index.js +547 -129
- package/dist/messaging.js +271 -98
- package/dist/messagingd.js +887 -71
- package/dist/tui.js +196 -103
- package/docs/architecture.md +29 -0
- package/docs/configuration.md +7 -1
- package/docs/security.md +25 -0
- package/package.json +1251 -8
- package/views/section.html +299 -0
package/dist/messagingd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import net, { BlockList, isIP } from "node:net";
|
|
5
5
|
import { KeyObject, createCipheriv, createDecipheriv, createHash, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, sign, verify } from "node:crypto";
|
|
6
6
|
import { homedir, tmpdir } from "node:os";
|
|
@@ -274,7 +274,7 @@ function partial(Class, schema, mask, name = "partial") {
|
|
|
274
274
|
checks: []
|
|
275
275
|
}));
|
|
276
276
|
}
|
|
277
|
-
function required(Class, schema, mask) {
|
|
277
|
+
function required$1(Class, schema, mask) {
|
|
278
278
|
const selected = mask ? new Set(maskedKeys(schema, mask)) : void 0;
|
|
279
279
|
const newShape = {};
|
|
280
280
|
mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)), (value, key) => selected && !selected.has(key) ? value : new Class({
|
|
@@ -5450,7 +5450,7 @@ var init_schemas = __esmMin((() => {
|
|
|
5450
5450
|
return partial(ZodExactOptional, this, args[0], "exactPartial");
|
|
5451
5451
|
},
|
|
5452
5452
|
required(...args) {
|
|
5453
|
-
return required(ZodNonOptional, this, args[0]);
|
|
5453
|
+
return required$1(ZodNonOptional, this, args[0]);
|
|
5454
5454
|
}
|
|
5455
5455
|
});
|
|
5456
5456
|
ZodUnion = $constructor("ZodUnion", (inst, def) => {
|
|
@@ -5943,7 +5943,8 @@ var init_jelly$3 = __esmMin((() => {
|
|
|
5943
5943
|
jti: string$1().regex(/^[\x21-\x7e]{1,128}$/, "printable jti"),
|
|
5944
5944
|
iss: string$1().max(2048).optional(),
|
|
5945
5945
|
dev: string$1().max(200).optional(),
|
|
5946
|
-
cls: string$1().max(32).optional()
|
|
5946
|
+
cls: string$1().max(32).optional(),
|
|
5947
|
+
mbr: string$1().max(200).optional()
|
|
5947
5948
|
});
|
|
5948
5949
|
JellySessionClaims = looseObject({
|
|
5949
5950
|
aud: string$1().min(1).max(2048),
|
|
@@ -6553,7 +6554,8 @@ var init_envelope$1 = __esmMin((() => {
|
|
|
6553
6554
|
label: MachineLabel,
|
|
6554
6555
|
sessions: array(SessionInfo).max(LIMITS.presenceSessions),
|
|
6555
6556
|
features: array(string$1().max(64)).max(32).optional(),
|
|
6556
|
-
direct: array(DirectEndpoint).min(1).max(4).optional()
|
|
6557
|
+
direct: array(DirectEndpoint).min(1).max(4).optional(),
|
|
6558
|
+
away: literal(true).optional()
|
|
6557
6559
|
});
|
|
6558
6560
|
PAYLOAD_BY_TYPE = {
|
|
6559
6561
|
message: MessagePayload,
|
|
@@ -6832,7 +6834,7 @@ function hostedRelayAuthPayload(args) {
|
|
|
6832
6834
|
ts: args.ts
|
|
6833
6835
|
};
|
|
6834
6836
|
}
|
|
6835
|
-
var Req, Size, Handle, Email, OrgId, DirBindFrame, HostedHelloFrame, DirAppendFrame, DirKeysFrame, DirSthFrame, DirLookupFrame, DirDeviceFrame, DirResolveFrame, DirSearchFrame, DirWatchFrame, PresenceWatchFrame, DirEraseFrame, HostedWelcomeFrame, DirOkFrame, DirSthIsFrame, DirPersonsFrame, ResolvedPerson, DirResolvedFrame, DirChangedFrame, DirUnboundFrame, DirectoryRelayFrame;
|
|
6837
|
+
var Req, Size, Handle, Email, OrgId, DirBindFrame, HostedHelloFrame, DirAppendFrame, DirKeysFrame, DirSthFrame, DirLookupFrame, DirDeviceFrame, DirResolveFrame, DirSearchFrame, DirWatchFrame, PresenceWatchFrame, DirEraseFrame, WAKE_TICKET_BYTES, WakeUrl, WakeArmFrame, WakeDisarmFrame, HostedWelcomeFrame, DirOkFrame, DirSthIsFrame, DirPersonsFrame, ResolvedPerson, DirResolvedFrame, DirChangedFrame, DirUnboundFrame, DirectoryRelayFrame;
|
|
6836
6838
|
var init_directory_frames = __esmMin((() => {
|
|
6837
6839
|
init_zod();
|
|
6838
6840
|
init_common();
|
|
@@ -6916,6 +6918,20 @@ var init_directory_frames = __esmMin((() => {
|
|
|
6916
6918
|
entries: array(LogEntry).min(1).max(LIMITS.devicesPerPerson),
|
|
6917
6919
|
token: Jws
|
|
6918
6920
|
});
|
|
6921
|
+
WAKE_TICKET_BYTES = 4096;
|
|
6922
|
+
WakeUrl = string$1().max(2048).regex(/^https?:\/\/[^/?#@\s]+\/hosts\/hst_[A-Za-z0-9._:-]{1,123}\/wake$/, "a Jelly ID wake URL");
|
|
6923
|
+
WakeArmFrame = object$1({
|
|
6924
|
+
t: literal("wake.arm"),
|
|
6925
|
+
req: Req,
|
|
6926
|
+
url: WakeUrl,
|
|
6927
|
+
ticket: Jws.refine((ticket) => ticket.length <= WAKE_TICKET_BYTES, { message: `at most ${WAKE_TICKET_BYTES} bytes` }),
|
|
6928
|
+
agents: array(SessionName).max(64),
|
|
6929
|
+
awaiting: array(Ulid).max(500)
|
|
6930
|
+
});
|
|
6931
|
+
WakeDisarmFrame = object$1({
|
|
6932
|
+
t: literal("wake.disarm"),
|
|
6933
|
+
req: Req
|
|
6934
|
+
});
|
|
6919
6935
|
discriminatedUnion("t", [
|
|
6920
6936
|
DirBindFrame,
|
|
6921
6937
|
HostedHelloFrame,
|
|
@@ -6928,7 +6944,9 @@ var init_directory_frames = __esmMin((() => {
|
|
|
6928
6944
|
DirSearchFrame,
|
|
6929
6945
|
DirWatchFrame,
|
|
6930
6946
|
PresenceWatchFrame,
|
|
6931
|
-
DirEraseFrame
|
|
6947
|
+
DirEraseFrame,
|
|
6948
|
+
WakeArmFrame,
|
|
6949
|
+
WakeDisarmFrame
|
|
6932
6950
|
]);
|
|
6933
6951
|
HostedWelcomeFrame = object$1({
|
|
6934
6952
|
t: literal("welcome"),
|
|
@@ -7046,7 +7064,7 @@ var init_direct$1 = __esmMin((() => {
|
|
|
7046
7064
|
]);
|
|
7047
7065
|
}));
|
|
7048
7066
|
// packages/core/dist/schema/ipc/device.js
|
|
7049
|
-
var BindingState, RealmAlarmSummary, DeviceStatus, DeviceListEntry, LinkText, Sas, LinkPreview, KeyFingerprint, LoginUserCode, LoginPurpose, LoginKind, LoginShown, LoginState, DEVICE_METHODS;
|
|
7067
|
+
var BindingState, RealmAlarmSummary, DeviceStatus, DeviceListEntry, LinkText, Sas, LinkPreview, KeyFingerprint, LoginUserCode, LoginPurpose, LoginKind, LoginShown, LoginState, DeviceRemoveParams, DeviceApproveParams, DEVICE_METHODS;
|
|
7050
7068
|
var init_device = __esmMin((() => {
|
|
7051
7069
|
init_zod();
|
|
7052
7070
|
init_common();
|
|
@@ -7143,6 +7161,19 @@ var init_device = __esmMin((() => {
|
|
|
7143
7161
|
purpose: LoginPurpose
|
|
7144
7162
|
})
|
|
7145
7163
|
]);
|
|
7164
|
+
DeviceRemoveParams = strictObject({
|
|
7165
|
+
device: MachineId,
|
|
7166
|
+
reason: _enum([
|
|
7167
|
+
"self",
|
|
7168
|
+
"removed",
|
|
7169
|
+
"lost"
|
|
7170
|
+
]).optional(),
|
|
7171
|
+
token: Jws.optional()
|
|
7172
|
+
});
|
|
7173
|
+
DeviceApproveParams = strictObject({
|
|
7174
|
+
link: LinkText,
|
|
7175
|
+
sas: Sas
|
|
7176
|
+
});
|
|
7146
7177
|
DEVICE_METHODS = {
|
|
7147
7178
|
"device.status": {
|
|
7148
7179
|
params: strictObject({}),
|
|
@@ -7165,15 +7196,7 @@ var init_device = __esmMin((() => {
|
|
|
7165
7196
|
result: object$1({ devices: array(DeviceListEntry) })
|
|
7166
7197
|
},
|
|
7167
7198
|
"device.remove": {
|
|
7168
|
-
params:
|
|
7169
|
-
device: MachineId,
|
|
7170
|
-
reason: _enum([
|
|
7171
|
-
"self",
|
|
7172
|
-
"removed",
|
|
7173
|
-
"lost"
|
|
7174
|
-
]).optional(),
|
|
7175
|
-
token: Jws.optional()
|
|
7176
|
-
}),
|
|
7199
|
+
params: DeviceRemoveParams,
|
|
7177
7200
|
result: object$1({ seq: number$1().int().nonnegative() })
|
|
7178
7201
|
},
|
|
7179
7202
|
"device.link": {
|
|
@@ -7210,14 +7233,11 @@ var init_device = __esmMin((() => {
|
|
|
7210
7233
|
result: object$1({ ok: literal(true) })
|
|
7211
7234
|
},
|
|
7212
7235
|
"device.approve.preview": {
|
|
7213
|
-
params:
|
|
7236
|
+
params: DeviceApproveParams.partial({ sas: true }),
|
|
7214
7237
|
result: LinkPreview
|
|
7215
7238
|
},
|
|
7216
7239
|
"device.approve": {
|
|
7217
|
-
params:
|
|
7218
|
-
link: LinkText,
|
|
7219
|
-
sas: Sas
|
|
7220
|
-
}),
|
|
7240
|
+
params: DeviceApproveParams,
|
|
7221
7241
|
result: object$1({
|
|
7222
7242
|
device: MachineId,
|
|
7223
7243
|
seq: number$1().int().nonnegative()
|
|
@@ -7361,7 +7381,7 @@ var init_shapes = __esmMin((() => {
|
|
|
7361
7381
|
});
|
|
7362
7382
|
}));
|
|
7363
7383
|
// packages/core/dist/schema/ipc/consent.js
|
|
7364
|
-
var GrantTarget, Levels, GrantView, GrantReview, GrantSetParams, AskRequest, ContactRequestView, ContactSuggestion, HeldRequest, RequestAnswer, RequestText, PersonReach, CONSENT_METHODS;
|
|
7384
|
+
var GrantTarget, Levels, GrantView, GrantReview, GrantSetParams, AskRequest, ContactRequestView, ContactSuggestion, HeldRequest, RequestAnswer, RequestText, PersonReach, RequestAnswerParams, ContactRequestParams, CONSENT_METHODS;
|
|
7365
7385
|
var init_consent$1 = __esmMin((() => {
|
|
7366
7386
|
init_zod();
|
|
7367
7387
|
init_common();
|
|
@@ -7460,6 +7480,16 @@ var init_consent$1 = __esmMin((() => {
|
|
|
7460
7480
|
source: _enum(["card", "default"]),
|
|
7461
7481
|
at: Millis.optional()
|
|
7462
7482
|
});
|
|
7483
|
+
RequestAnswerParams = strictObject({
|
|
7484
|
+
id: Ulid,
|
|
7485
|
+
answer: RequestAnswer,
|
|
7486
|
+
all: boolean().optional()
|
|
7487
|
+
});
|
|
7488
|
+
ContactRequestParams = strictObject({
|
|
7489
|
+
email: Email,
|
|
7490
|
+
text: RequestText,
|
|
7491
|
+
suggestion: Ulid.optional()
|
|
7492
|
+
});
|
|
7463
7493
|
CONSENT_METHODS = {
|
|
7464
7494
|
"grant.list": {
|
|
7465
7495
|
params: strictObject({ subject: GrantTarget.optional() }),
|
|
@@ -7481,19 +7511,35 @@ var init_consent$1 = __esmMin((() => {
|
|
|
7481
7511
|
result: object$1({ requests: array(HeldRequest).max(LIMITS.heldTotal * 3) })
|
|
7482
7512
|
},
|
|
7483
7513
|
"request.answer": {
|
|
7484
|
-
params:
|
|
7485
|
-
id: Ulid,
|
|
7486
|
-
answer: RequestAnswer,
|
|
7487
|
-
all: boolean().optional()
|
|
7488
|
-
}),
|
|
7514
|
+
params: RequestAnswerParams,
|
|
7489
7515
|
result: object$1({ answered: array(Ulid).max(LIMITS.heldTotal) })
|
|
7490
7516
|
},
|
|
7517
|
+
"request.answer.preview": {
|
|
7518
|
+
params: RequestAnswerParams,
|
|
7519
|
+
result: object$1({
|
|
7520
|
+
kind: _enum([
|
|
7521
|
+
"message",
|
|
7522
|
+
"task",
|
|
7523
|
+
"contact",
|
|
7524
|
+
"suggestion"
|
|
7525
|
+
]),
|
|
7526
|
+
from: object$1({
|
|
7527
|
+
name: string$1().max(200),
|
|
7528
|
+
email: string$1().max(320).optional(),
|
|
7529
|
+
org: _enum([
|
|
7530
|
+
"same",
|
|
7531
|
+
"other",
|
|
7532
|
+
"none"
|
|
7533
|
+
])
|
|
7534
|
+
}),
|
|
7535
|
+
agent: SessionName.optional(),
|
|
7536
|
+
to: SessionName.optional(),
|
|
7537
|
+
count: number$1().int().positive(),
|
|
7538
|
+
sentence: string$1().max(500)
|
|
7539
|
+
})
|
|
7540
|
+
},
|
|
7491
7541
|
"contact.request": {
|
|
7492
|
-
params:
|
|
7493
|
-
email: Email,
|
|
7494
|
-
text: RequestText,
|
|
7495
|
-
suggestion: Ulid.optional()
|
|
7496
|
-
}),
|
|
7542
|
+
params: ContactRequestParams,
|
|
7497
7543
|
result: object$1({
|
|
7498
7544
|
id: Ulid,
|
|
7499
7545
|
conversation: RoomRef,
|
|
@@ -7501,7 +7547,7 @@ var init_consent$1 = __esmMin((() => {
|
|
|
7501
7547
|
})
|
|
7502
7548
|
},
|
|
7503
7549
|
"contact.request.preview": {
|
|
7504
|
-
params:
|
|
7550
|
+
params: ContactRequestParams.partial({ text: true }),
|
|
7505
7551
|
result: object$1({ found: boolean() })
|
|
7506
7552
|
},
|
|
7507
7553
|
"card.get": {
|
|
@@ -7680,7 +7726,8 @@ var init_surfaces = __esmMin((() => {
|
|
|
7680
7726
|
result: PersonSummary.extend({ devices: array(object$1({
|
|
7681
7727
|
device: MachineId,
|
|
7682
7728
|
label: MachineLabel,
|
|
7683
|
-
self: boolean()
|
|
7729
|
+
self: boolean(),
|
|
7730
|
+
away: literal(true).optional()
|
|
7684
7731
|
})) })
|
|
7685
7732
|
}
|
|
7686
7733
|
};
|
|
@@ -7688,7 +7735,10 @@ var init_surfaces = __esmMin((() => {
|
|
|
7688
7735
|
var init_jelly$2 = __esmMin((() => {
|
|
7689
7736
|
init_zod();
|
|
7690
7737
|
init_common();
|
|
7738
|
+
init_directory_frames();
|
|
7739
|
+
init_jelly$3();
|
|
7691
7740
|
init_directory$2();
|
|
7741
|
+
init_device();
|
|
7692
7742
|
init_shapes();
|
|
7693
7743
|
init_surfaces();
|
|
7694
7744
|
NoticeId = string$1().regex(/^[mra]_[0-9A-Za-z]{1,64}$/, "notice id");
|
|
@@ -7804,8 +7854,45 @@ var init_jelly$2 = __esmMin((() => {
|
|
|
7804
7854
|
params: strictObject({ ids: array(NoticeId).min(1).max(200) }),
|
|
7805
7855
|
result: object$1({ ok: literal(true) })
|
|
7806
7856
|
},
|
|
7857
|
+
"daemon.woke": {
|
|
7858
|
+
params: strictObject({}),
|
|
7859
|
+
result: object$1({ ok: literal(true) })
|
|
7860
|
+
},
|
|
7861
|
+
"person.attached": {
|
|
7862
|
+
params: strictObject({ attached: boolean() }),
|
|
7863
|
+
result: object$1({ ok: literal(true) })
|
|
7864
|
+
},
|
|
7865
|
+
"wake.arm": {
|
|
7866
|
+
params: strictObject({
|
|
7867
|
+
url: WakeUrl,
|
|
7868
|
+
ticket: Jws.refine((ticket) => ticket.length <= WAKE_TICKET_BYTES, { message: `at most ${WAKE_TICKET_BYTES} bytes` }),
|
|
7869
|
+
agents: array(SessionName).max(64)
|
|
7870
|
+
}),
|
|
7871
|
+
result: object$1({
|
|
7872
|
+
relay: _enum(["armed", "pending"]),
|
|
7873
|
+
awaiting: number$1().int().nonnegative()
|
|
7874
|
+
})
|
|
7875
|
+
},
|
|
7876
|
+
"wake.disarm": {
|
|
7877
|
+
params: strictObject({}),
|
|
7878
|
+
result: object$1({ relay: _enum(["disarmed", "pending"]) })
|
|
7879
|
+
},
|
|
7880
|
+
"daemon.snapshot": {
|
|
7881
|
+
params: strictObject({ done: literal(true).optional() }),
|
|
7882
|
+
result: union$1([object$1({
|
|
7883
|
+
folder: string$1().max(4096),
|
|
7884
|
+
files: array(object$1({
|
|
7885
|
+
name: string$1().max(64),
|
|
7886
|
+
bytes: number$1().int().nonnegative(),
|
|
7887
|
+
sha256: string$1().regex(/^[0-9a-f]{64}$/)
|
|
7888
|
+
})).max(8),
|
|
7889
|
+
storeSchema: number$1().int().positive(),
|
|
7890
|
+
at: Millis,
|
|
7891
|
+
until: Millis
|
|
7892
|
+
}), object$1({ released: boolean() })])
|
|
7893
|
+
},
|
|
7807
7894
|
"device.remove.preview": {
|
|
7808
|
-
params:
|
|
7895
|
+
params: DeviceRemoveParams,
|
|
7809
7896
|
result: object$1({
|
|
7810
7897
|
label: MachineLabel,
|
|
7811
7898
|
kind: DeviceKind.optional(),
|
|
@@ -7820,7 +7907,7 @@ var init_jelly$2 = __esmMin((() => {
|
|
|
7820
7907
|
})
|
|
7821
7908
|
}
|
|
7822
7909
|
};
|
|
7823
|
-
})), MESSAGING_ERROR_RPC_CODE, SessionHost, Ok, Empty, DaemonStatus, InviteSummary, IPC_METHODS, AGENT_METHODS, HOST_SCOPE_METHODS, IpcEvent;
|
|
7910
|
+
})), MESSAGING_ERROR_RPC_CODE, SessionHost, Ok, Empty, DaemonStatus, InviteSummary, IPC_METHODS, AGENT_METHODS, HUMAN_METHODS, ADMIN_METHODS, HOST_SCOPE_METHODS, IpcEvent;
|
|
7824
7911
|
var init_ipc = __esmMin((() => {
|
|
7825
7912
|
init_zod();
|
|
7826
7913
|
init_device();
|
|
@@ -8195,11 +8282,73 @@ var init_ipc = __esmMin((() => {
|
|
|
8195
8282
|
"task.update",
|
|
8196
8283
|
"task.cancel"
|
|
8197
8284
|
];
|
|
8285
|
+
HUMAN_METHODS = [
|
|
8286
|
+
"conv.list",
|
|
8287
|
+
"conv.read",
|
|
8288
|
+
"conv.markRead",
|
|
8289
|
+
"conv.subscribe",
|
|
8290
|
+
"person.send",
|
|
8291
|
+
"people.search",
|
|
8292
|
+
"people.get",
|
|
8293
|
+
"grant.list",
|
|
8294
|
+
"grant.set",
|
|
8295
|
+
"grant.set.preview",
|
|
8296
|
+
"request.list",
|
|
8297
|
+
"request.answer",
|
|
8298
|
+
"request.answer.preview",
|
|
8299
|
+
"contact.request",
|
|
8300
|
+
"contact.request.preview",
|
|
8301
|
+
"card.get",
|
|
8302
|
+
"agents.list",
|
|
8303
|
+
"agents.read",
|
|
8304
|
+
"notice.subscribe",
|
|
8305
|
+
"notice.list",
|
|
8306
|
+
"notice.ack",
|
|
8307
|
+
"device.approve.preview",
|
|
8308
|
+
"device.remove.preview",
|
|
8309
|
+
"channel.create",
|
|
8310
|
+
"channel.invite",
|
|
8311
|
+
"channel.leave",
|
|
8312
|
+
"channel.remove"
|
|
8313
|
+
];
|
|
8314
|
+
ADMIN_METHODS = [
|
|
8315
|
+
"daemon.stop",
|
|
8316
|
+
"daemon.woke",
|
|
8317
|
+
"person.attached",
|
|
8318
|
+
"daemon.snapshot",
|
|
8319
|
+
"wake.arm",
|
|
8320
|
+
"wake.disarm",
|
|
8321
|
+
"device.list",
|
|
8322
|
+
"device.remove",
|
|
8323
|
+
"device.link",
|
|
8324
|
+
"device.login",
|
|
8325
|
+
"device.login.status",
|
|
8326
|
+
"device.login.cancel",
|
|
8327
|
+
"device.approve",
|
|
8328
|
+
"identity.rotateEnc",
|
|
8329
|
+
"team.create",
|
|
8330
|
+
"team.invite",
|
|
8331
|
+
"team.join",
|
|
8332
|
+
"team.members",
|
|
8333
|
+
"team.remove",
|
|
8334
|
+
"team.role",
|
|
8335
|
+
"team.revokeInvite",
|
|
8336
|
+
"team.add",
|
|
8337
|
+
"team.pair",
|
|
8338
|
+
"team.claim",
|
|
8339
|
+
"team.pairCancel",
|
|
8340
|
+
"team.prepare"
|
|
8341
|
+
];
|
|
8198
8342
|
HOST_SCOPE_METHODS = [
|
|
8199
8343
|
"notice.subscribe",
|
|
8200
8344
|
"notice.list",
|
|
8201
8345
|
"conv.list",
|
|
8202
|
-
"daemon.stop"
|
|
8346
|
+
"daemon.stop",
|
|
8347
|
+
"daemon.woke",
|
|
8348
|
+
"person.attached",
|
|
8349
|
+
"daemon.snapshot",
|
|
8350
|
+
"wake.arm",
|
|
8351
|
+
"wake.disarm"
|
|
8203
8352
|
];
|
|
8204
8353
|
IpcEvent = discriminatedUnion("type", [
|
|
8205
8354
|
object$1({
|
|
@@ -8297,7 +8446,7 @@ var init_b64u = __esmMin((() => {
|
|
|
8297
8446
|
// packages/core/dist/version.js
|
|
8298
8447
|
var VERSION;
|
|
8299
8448
|
var init_version$1 = __esmMin((() => {
|
|
8300
|
-
VERSION = "0.
|
|
8449
|
+
VERSION = "0.5.0";
|
|
8301
8450
|
}));
|
|
8302
8451
|
// packages/core/dist/jcs.js
|
|
8303
8452
|
function hasLoneSurrogate(text) {
|
|
@@ -8808,7 +8957,7 @@ function seal(args, randomness) {
|
|
|
8808
8957
|
const payload = parseWith(PAYLOAD_BY_TYPE[type], args.payload, `${type} payload`);
|
|
8809
8958
|
const plaintext = utf8(canonicalize(payload));
|
|
8810
8959
|
if (plaintext.length > LIMITS.plaintextBytes) throw new MessagingError("too_large", `payload exceeds ${LIMITS.plaintextBytes} bytes`);
|
|
8811
|
-
const klass = type === "message" && payload.conv?.kind === "request" ? "request" : classOf(args.from, args.to);
|
|
8960
|
+
const klass = type === "message" && payload.conv?.kind === "request" ? "request" : classOf$1(args.from, args.to);
|
|
8812
8961
|
assertMeaning(type, args.from, args.to, klass, payload);
|
|
8813
8962
|
if (type !== "presence") return sealBytes(args, type, klass, plaintext, randomness);
|
|
8814
8963
|
if (args.recipientEnc !== void 0) throw new MessagingError("invalid_request", "presence is not sealed");
|
|
@@ -8849,7 +8998,7 @@ function base(args, type, klass) {
|
|
|
8849
8998
|
...args.replyTo === void 0 ? {} : { replyTo: args.replyTo }
|
|
8850
8999
|
};
|
|
8851
9000
|
}
|
|
8852
|
-
function classOf(from, to) {
|
|
9001
|
+
function classOf$1(from, to) {
|
|
8853
9002
|
return to !== void 0 && "person" in to && to.person === from?.person ? "self" : "contact";
|
|
8854
9003
|
}
|
|
8855
9004
|
function sealBytes(args, type, klass, plaintext, randomness) {
|
|
@@ -9024,12 +9173,12 @@ function shapeProblem(env) {
|
|
|
9024
9173
|
if (env.class === void 0) return "a sealed envelope needs class";
|
|
9025
9174
|
if (env.class === "request") {
|
|
9026
9175
|
if (env.type !== "message") return "only a message is of class request";
|
|
9027
|
-
if (classOf(env.from, env.to) !== "contact") return "a message request goes to another person";
|
|
9176
|
+
if (classOf$1(env.from, env.to) !== "contact") return "a message request goes to another person";
|
|
9028
9177
|
if (env.from.agent !== void 0 || "agent" in env.to && env.to.agent !== void 0) return "a message request goes from a person to a person";
|
|
9029
9178
|
if (env.replyTo !== void 0) return "a message request answers nothing";
|
|
9030
9179
|
return;
|
|
9031
9180
|
}
|
|
9032
|
-
if (env.class !== classOf(env.from, env.to)) return `class ${env.class} does not match the ends`;
|
|
9181
|
+
if (env.class !== classOf$1(env.from, env.to)) return `class ${env.class} does not match the ends`;
|
|
9033
9182
|
}
|
|
9034
9183
|
function isPlainObject(value) {
|
|
9035
9184
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -9506,6 +9655,9 @@ var init_roster = __esmMin((() => {
|
|
|
9506
9655
|
};
|
|
9507
9656
|
}));
|
|
9508
9657
|
// packages/core/dist/jelly-host.js
|
|
9658
|
+
function actionClass(method) {
|
|
9659
|
+
return Object.hasOwn(ACTION_CLASSES, method) ? ACTION_CLASSES[method] : "local";
|
|
9660
|
+
}
|
|
9509
9661
|
function grantInput(params) {
|
|
9510
9662
|
return sha256(canonicalize(params ?? {}));
|
|
9511
9663
|
}
|
|
@@ -9567,12 +9719,18 @@ function verifyActGrant(token, check) {
|
|
|
9567
9719
|
if (claims.in === void 0 || claims.in !== grantInput(check.params)) throw fail("is for other params");
|
|
9568
9720
|
checkTimes(claims.iat, claims.exp, 60, check.now, fail);
|
|
9569
9721
|
}
|
|
9570
|
-
if (claims.
|
|
9722
|
+
if (claims.act === "host") {
|
|
9723
|
+
if (claims.cls !== void 0) throw fail("is host scope, which names no action class");
|
|
9724
|
+
} else {
|
|
9725
|
+
if (claims.cls === void 0) throw fail("names no action class");
|
|
9726
|
+
if (claims.cls !== actionClass(check.method)) throw fail(`names action class ${claims.cls}, not ${actionClass(check.method)}`);
|
|
9727
|
+
}
|
|
9571
9728
|
return {
|
|
9572
9729
|
scope: claims.act === "host" ? "host" : "action",
|
|
9573
9730
|
jti: claims.jti,
|
|
9574
9731
|
exp: claims.exp,
|
|
9575
|
-
dev: claims.dev
|
|
9732
|
+
dev: claims.dev,
|
|
9733
|
+
mbr: claims.mbr
|
|
9576
9734
|
};
|
|
9577
9735
|
}
|
|
9578
9736
|
function verifySessionTicket(token, key, now) {
|
|
@@ -9608,7 +9766,7 @@ function parseActKey(text) {
|
|
|
9608
9766
|
}
|
|
9609
9767
|
return parsed.data;
|
|
9610
9768
|
}
|
|
9611
|
-
var SESSION_TICKET_MAX_S,
|
|
9769
|
+
var SESSION_TICKET_MAX_S, CHAT_METHODS, VIEW_METHODS, classOf, ACTION_CLASSES;
|
|
9612
9770
|
var init_jelly_host = __esmMin((() => {
|
|
9613
9771
|
init_b64u();
|
|
9614
9772
|
init_hash();
|
|
@@ -9617,8 +9775,29 @@ var init_jelly_host = __esmMin((() => {
|
|
|
9617
9775
|
init_wire();
|
|
9618
9776
|
init_errors();
|
|
9619
9777
|
init_jelly$3();
|
|
9778
|
+
init_ipc();
|
|
9620
9779
|
SESSION_TICKET_MAX_S = 604800;
|
|
9621
|
-
|
|
9780
|
+
CHAT_METHODS = [
|
|
9781
|
+
"person.send",
|
|
9782
|
+
"conv.markRead",
|
|
9783
|
+
"notice.ack"
|
|
9784
|
+
];
|
|
9785
|
+
VIEW_METHODS = [
|
|
9786
|
+
"conv.list",
|
|
9787
|
+
"conv.read",
|
|
9788
|
+
"conv.subscribe",
|
|
9789
|
+
"people.search",
|
|
9790
|
+
"people.get",
|
|
9791
|
+
"grant.list",
|
|
9792
|
+
"request.list",
|
|
9793
|
+
"agents.list",
|
|
9794
|
+
"agents.read",
|
|
9795
|
+
"notice.subscribe",
|
|
9796
|
+
"notice.list",
|
|
9797
|
+
"device.list"
|
|
9798
|
+
];
|
|
9799
|
+
classOf = (method) => CHAT_METHODS.includes(method) ? "chat" : VIEW_METHODS.includes(method) ? "view" : "local";
|
|
9800
|
+
ACTION_CLASSES = Object.freeze(Object.fromEntries([...HUMAN_METHODS, ...ADMIN_METHODS].map((method) => [method, classOf(method)])));
|
|
9622
9801
|
}));
|
|
9623
9802
|
// packages/core/dist/directory/person.js
|
|
9624
9803
|
function base32Lower(bytes) {
|
|
@@ -10672,7 +10851,7 @@ function verifyPersonView(view, context) {
|
|
|
10672
10851
|
const { sth, known } = context;
|
|
10673
10852
|
if (known && known.person !== parsed.person) throw new MessagingError("invalid_request", "the known state is another person's");
|
|
10674
10853
|
const head = verifyEpochProof(sth, context.head);
|
|
10675
|
-
if (head.index !== sth.size - 1) throw fail("the head epoch is not the tree head's last");
|
|
10854
|
+
if (head.index !== sth.size - 1) throw fail$1("the head epoch is not the tree head's last");
|
|
10676
10855
|
const mapRoot = head.leaf.mapRoot;
|
|
10677
10856
|
const logContext = {
|
|
10678
10857
|
...context,
|
|
@@ -10697,7 +10876,7 @@ function verifyPersonView(view, context) {
|
|
|
10697
10876
|
let start = held;
|
|
10698
10877
|
if (parsed.log.length > 0) {
|
|
10699
10878
|
const first = parsed.log[0].seq;
|
|
10700
|
-
if (first > held) throw fail("the log skips entries after those held");
|
|
10879
|
+
if (first > held) throw fail$1("the log skips entries after those held");
|
|
10701
10880
|
for (const entry of parsed.log.slice(0, Math.max(0, held - first))) {
|
|
10702
10881
|
const ours = log.entries()[entry.seq];
|
|
10703
10882
|
if (logEntryHash(ours) !== logEntryHash(entry)) throw new DirectoryInconsistency("the log differs from the one held", {
|
|
@@ -10743,7 +10922,7 @@ function verifyPersonView(view, context) {
|
|
|
10743
10922
|
ours: ours.seq,
|
|
10744
10923
|
theirs: statement.seq
|
|
10745
10924
|
});
|
|
10746
|
-
if (ours && statement.seq === ours.seq && keysStatementHash(statement) !== keysStatementHash(ours)) throw fail("two keys statements share a device and seq");
|
|
10925
|
+
if (ours && statement.seq === ours.seq && keysStatementHash(statement) !== keysStatementHash(ours)) throw fail$1("two keys statements share a device and seq");
|
|
10747
10926
|
keys.set(statement.device, statement);
|
|
10748
10927
|
}
|
|
10749
10928
|
for (const [device, ours] of known?.keys ?? []) if (log.device(device)?.current && !keys.has(device)) throw new DirectoryInconsistency("a held keys statement is withheld", {
|
|
@@ -10783,11 +10962,11 @@ function verifyPersonView(view, context) {
|
|
|
10783
10962
|
function checkWitness(witness, seq, deadline, log, person, sth) {
|
|
10784
10963
|
const epoch = verifyEpochProof(sth, witness.epoch);
|
|
10785
10964
|
const committed = witness.value.log;
|
|
10786
|
-
if (committed.seq < seq || log.hashAt(committed.seq) !== committed.hash) throw fail("an epoch witness commits to another log");
|
|
10787
|
-
if (!verifyMapProof(epoch.leaf.mapRoot, person, mapValue(witness.value), witness.mapProof)) throw fail("an epoch witness's map proof does not verify");
|
|
10965
|
+
if (committed.seq < seq || log.hashAt(committed.seq) !== committed.hash) throw fail$1("an epoch witness commits to another log");
|
|
10966
|
+
if (!verifyMapProof(epoch.leaf.mapRoot, person, mapValue(witness.value), witness.mapProof)) throw fail$1("an epoch witness's map proof does not verify");
|
|
10788
10967
|
if (epoch.leaf.ts > deadline) throw new MessagingError("binding_invalid", "a Jelly binding first appeared after its token's life");
|
|
10789
10968
|
}
|
|
10790
|
-
var DirectoryInconsistency, fail;
|
|
10969
|
+
var DirectoryInconsistency, fail$1;
|
|
10791
10970
|
var init_person_view = __esmMin((() => {
|
|
10792
10971
|
init_check();
|
|
10793
10972
|
init_directory$2();
|
|
@@ -10804,7 +10983,7 @@ var init_person_view = __esmMin((() => {
|
|
|
10804
10983
|
this.evidence = evidence;
|
|
10805
10984
|
}
|
|
10806
10985
|
};
|
|
10807
|
-
fail = (what) => new MessagingError("directory_inconsistent", what);
|
|
10986
|
+
fail$1 = (what) => new MessagingError("directory_inconsistent", what);
|
|
10808
10987
|
}));
|
|
10809
10988
|
// packages/core/dist/address.js
|
|
10810
10989
|
function parseAddress(text) {
|
|
@@ -12825,6 +13004,7 @@ var init_dist$3 = __esmMin((() => {
|
|
|
12825
13004
|
init_contact_card();
|
|
12826
13005
|
init_delegation$1();
|
|
12827
13006
|
init_task();
|
|
13007
|
+
init_a2a();
|
|
12828
13008
|
init_direct();
|
|
12829
13009
|
init_peer_text();
|
|
12830
13010
|
init_agent_tools();
|
|
@@ -13271,7 +13451,7 @@ async function createIpcServer(options) {
|
|
|
13271
13451
|
method: "hello",
|
|
13272
13452
|
params: {
|
|
13273
13453
|
daemon: "messagingd",
|
|
13274
|
-
version: options.version ?? "0.
|
|
13454
|
+
version: options.version ?? "0.5.0",
|
|
13275
13455
|
protocol: 1,
|
|
13276
13456
|
pid: process.pid,
|
|
13277
13457
|
...options.deployment ? { deployment: options.deployment } : {}
|
|
@@ -14288,7 +14468,7 @@ var init_keychain = __esmMin((() => {
|
|
|
14288
14468
|
}));
|
|
14289
14469
|
// packages/daemon/dist/identity/store.js
|
|
14290
14470
|
var FileStore;
|
|
14291
|
-
var init_store$
|
|
14471
|
+
var init_store$3 = __esmMin((() => {
|
|
14292
14472
|
init_dist$3();
|
|
14293
14473
|
init_record();
|
|
14294
14474
|
FileStore = class {
|
|
@@ -14365,7 +14545,7 @@ var init_identity = __esmMin((() => {
|
|
|
14365
14545
|
init_home();
|
|
14366
14546
|
init_keychain();
|
|
14367
14547
|
init_record();
|
|
14368
|
-
init_store$
|
|
14548
|
+
init_store$3();
|
|
14369
14549
|
}));
|
|
14370
14550
|
// packages/daemon/dist/pairing.js
|
|
14371
14551
|
function pairingPath(home) {
|
|
@@ -15509,7 +15689,8 @@ function presencePayload(rows, label, seq, extra, policy) {
|
|
|
15509
15689
|
label,
|
|
15510
15690
|
sessions: [],
|
|
15511
15691
|
features: [...FEATURES, ...extra?.features ?? []],
|
|
15512
|
-
...extra?.direct ? { direct: extra.direct } : {}
|
|
15692
|
+
...extra?.direct ? { direct: extra.direct } : {},
|
|
15693
|
+
...policy.away === true ? { away: true } : {}
|
|
15513
15694
|
};
|
|
15514
15695
|
return {
|
|
15515
15696
|
...payload,
|
|
@@ -17142,6 +17323,11 @@ var init_directory_home = __esmMin((() => {
|
|
|
17142
17323
|
this.#options.log("info", "directory.approved", { seq: done.seq });
|
|
17143
17324
|
return done;
|
|
17144
17325
|
}
|
|
17326
|
+
async wake(state) {
|
|
17327
|
+
const link = this.#link;
|
|
17328
|
+
if (!link?.wake) return "pending";
|
|
17329
|
+
return link.wake(state);
|
|
17330
|
+
}
|
|
17145
17331
|
presenceWatch(persons) {
|
|
17146
17332
|
this.#link?.presenceWatch(persons).catch((error) => this.#failed("directory.presence.watch", error));
|
|
17147
17333
|
}
|
|
@@ -17156,6 +17342,7 @@ var init_directory_home = __esmMin((() => {
|
|
|
17156
17342
|
},
|
|
17157
17343
|
send: (env) => home.#link ? home.#link.send(env) : Promise.reject(noLink()),
|
|
17158
17344
|
presence: (env) => home.#link?.presence(env),
|
|
17345
|
+
retryNow: () => home.#link?.retryNow?.(),
|
|
17159
17346
|
appendRoster: () => Promise.reject(new MessagingError("invalid_state", "a directory realm has no roster")),
|
|
17160
17347
|
syncRoster: () => Promise.reject(new MessagingError("invalid_state", "a directory realm has no roster"))
|
|
17161
17348
|
};
|
|
@@ -17261,7 +17448,8 @@ var init_jelly$1 = __esmMin((() => {
|
|
|
17261
17448
|
this.#deps.log("info", "jelly.grant", {
|
|
17262
17449
|
method: call.method,
|
|
17263
17450
|
scope: grant.scope,
|
|
17264
|
-
...grant.dev !== void 0 ? { dev: grant.dev.slice(0, 200) } : {}
|
|
17451
|
+
...grant.dev !== void 0 ? { dev: grant.dev.slice(0, 200) } : {},
|
|
17452
|
+
...grant.mbr !== void 0 ? { mbr: grant.mbr.slice(0, 200) } : {}
|
|
17265
17453
|
});
|
|
17266
17454
|
return {
|
|
17267
17455
|
scope: grant.scope,
|
|
@@ -17294,6 +17482,313 @@ var init_jelly$1 = __esmMin((() => {
|
|
|
17294
17482
|
}
|
|
17295
17483
|
};
|
|
17296
17484
|
}));
|
|
17485
|
+
// packages/daemon/dist/jelly-env.js
|
|
17486
|
+
function jellyEnvNamed(env) {
|
|
17487
|
+
return JELLY_ENV.filter((name) => (env[name] ?? "") !== "");
|
|
17488
|
+
}
|
|
17489
|
+
function fail(message) {
|
|
17490
|
+
throw new MessagingError("invalid_request", message);
|
|
17491
|
+
}
|
|
17492
|
+
function required(env, name) {
|
|
17493
|
+
const value = env[name] ?? "";
|
|
17494
|
+
if (value === "") fail(`${name} is not set: a Jelly host sets it with JELLY_ACTION_KEY`);
|
|
17495
|
+
return value;
|
|
17496
|
+
}
|
|
17497
|
+
function folder(name, value, separator = delimiter) {
|
|
17498
|
+
if (!isAbsolute(value)) fail(`${name} is not an absolute path`);
|
|
17499
|
+
if (value.includes(separator)) fail(`${name} names more than one folder (joined with "${separator}"); the daemon takes one`);
|
|
17500
|
+
return value;
|
|
17501
|
+
}
|
|
17502
|
+
function relayUrlOf(url) {
|
|
17503
|
+
let parsed;
|
|
17504
|
+
try {
|
|
17505
|
+
parsed = new URL(url);
|
|
17506
|
+
} catch {
|
|
17507
|
+
return fail(`JELLY_RESOURCES' ${JELLY_RESOURCE_NAME} url is not a URL`);
|
|
17508
|
+
}
|
|
17509
|
+
const loopback = /^(?:localhost|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[::1\])$/.test(parsed.hostname);
|
|
17510
|
+
const scheme = parsed.protocol === "https:" ? "wss:" : parsed.protocol === "http:" && loopback ? "ws:" : void 0;
|
|
17511
|
+
if (scheme === void 0) fail(`JELLY_RESOURCES' ${JELLY_RESOURCE_NAME} url is not https:// (or http:// to a loopback host)`);
|
|
17512
|
+
if (parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "" || parsed.pathname !== "/" && parsed.pathname !== "") fail(`JELLY_RESOURCES' ${JELLY_RESOURCE_NAME} url is not an origin: the relay is derived from its origin alone`);
|
|
17513
|
+
return `${scheme}//${parsed.host}/v1/connect`;
|
|
17514
|
+
}
|
|
17515
|
+
function jellyEnvRealm(env) {
|
|
17516
|
+
let raw;
|
|
17517
|
+
try {
|
|
17518
|
+
raw = parseWire(required(env, "JELLY_RESOURCES"), { maxBytes: 65536 });
|
|
17519
|
+
} catch (error) {
|
|
17520
|
+
if (error instanceof MessagingError && /JELLY_RESOURCES is not set/.test(error.message)) throw error;
|
|
17521
|
+
return fail("JELLY_RESOURCES is not JSON");
|
|
17522
|
+
}
|
|
17523
|
+
const list = Resources.safeParse(raw);
|
|
17524
|
+
if (!list.success) fail("JELLY_RESOURCES is not a list of discovery entries");
|
|
17525
|
+
const entries = list.data.map((item) => ResourceEntry.safeParse(item)).filter((parsed) => parsed.success && parsed.data.name === "messaging");
|
|
17526
|
+
if (entries.length === 0) fail(`JELLY_RESOURCES has no ${JELLY_RESOURCE_NAME} entry`);
|
|
17527
|
+
if (entries.length > 1) fail(`JELLY_RESOURCES has more than one ${JELLY_RESOURCE_NAME} entry`);
|
|
17528
|
+
const entry = entries[0].data;
|
|
17529
|
+
const realm = DirectoryRealmConfig.strict().safeParse({
|
|
17530
|
+
kind: "directory",
|
|
17531
|
+
url: relayUrlOf(entry.url),
|
|
17532
|
+
issuer: required(env, "JELLY_ID_ISSUER"),
|
|
17533
|
+
resource: entry.resource,
|
|
17534
|
+
key: required(env, "JELLY_SERVICE_ANCHOR")
|
|
17535
|
+
});
|
|
17536
|
+
if (!realm.success) {
|
|
17537
|
+
const at = realm.error.issues[0]?.path[0];
|
|
17538
|
+
fail(at === "issuer" ? "JELLY_ID_ISSUER is not an https:// issuer" : at === "key" ? "JELLY_SERVICE_ANCHOR is not an Ed25519 key in base64url" : `JELLY_RESOURCES' ${JELLY_RESOURCE_NAME} entry is invalid at ${String(at)}`);
|
|
17539
|
+
}
|
|
17540
|
+
try {
|
|
17541
|
+
directoryRealmId(realm.data);
|
|
17542
|
+
} catch {
|
|
17543
|
+
fail("JELLY_SERVICE_ANCHOR is not a usable Ed25519 key");
|
|
17544
|
+
}
|
|
17545
|
+
return realm.data;
|
|
17546
|
+
}
|
|
17547
|
+
function readJellyEnv(env) {
|
|
17548
|
+
const actKeyPath = required(env, "JELLY_ACTION_KEY");
|
|
17549
|
+
if (!isAbsolute(actKeyPath)) fail("JELLY_ACTION_KEY is not an absolute path");
|
|
17550
|
+
const home = folder("STATE_DIRECTORY", required(env, "STATE_DIRECTORY"));
|
|
17551
|
+
const runText = env.RUNTIME_DIRECTORY ?? "";
|
|
17552
|
+
const groupText = env.JELLY_SOCKET_GROUP ?? "";
|
|
17553
|
+
if (groupText !== "" && process.platform === "win32") fail("JELLY_SOCKET_GROUP is not supported on Windows: a named pipe is not shared with a group");
|
|
17554
|
+
if (groupText !== "" && (!/^\d{1,10}$/.test(groupText) || Number(groupText) > 4294967294)) fail("JELLY_SOCKET_GROUP is not a numeric gid");
|
|
17555
|
+
let label;
|
|
17556
|
+
try {
|
|
17557
|
+
label = jellyLabel(required(env, "JELLY_DEVICE_LABEL"));
|
|
17558
|
+
} catch (error) {
|
|
17559
|
+
if (error instanceof MessagingError && /is not set/.test(error.message)) throw error;
|
|
17560
|
+
return fail("JELLY_DEVICE_LABEL has no letter or digit to make a device label from");
|
|
17561
|
+
}
|
|
17562
|
+
return {
|
|
17563
|
+
home,
|
|
17564
|
+
...runText !== "" ? { run: folder("RUNTIME_DIRECTORY", runText) } : {},
|
|
17565
|
+
actKeyPath,
|
|
17566
|
+
label,
|
|
17567
|
+
...groupText !== "" ? { socketGroup: Number(groupText) } : {},
|
|
17568
|
+
realm: jellyEnvRealm(env)
|
|
17569
|
+
};
|
|
17570
|
+
}
|
|
17571
|
+
function realmMismatch(pinned, named) {
|
|
17572
|
+
return [
|
|
17573
|
+
"url",
|
|
17574
|
+
"issuer",
|
|
17575
|
+
"resource",
|
|
17576
|
+
"key"
|
|
17577
|
+
].filter((member) => pinned[member] !== named[member]);
|
|
17578
|
+
}
|
|
17579
|
+
async function pinRealm(realm, get = wellKnownGet) {
|
|
17580
|
+
const origin = new URL(realm.url);
|
|
17581
|
+
const url = `${origin.protocol === "wss:" ? "https:" : "http:"}//${origin.host}/.well-known/messaging.json`;
|
|
17582
|
+
let text;
|
|
17583
|
+
try {
|
|
17584
|
+
text = await get(url);
|
|
17585
|
+
} catch (error) {
|
|
17586
|
+
throw new MessagingError("relay_unavailable", `cannot read the relay's realm document ${url} for the first start (${error instanceof Error ? error.message.slice(0, 200) : "failed"}); the next start tries again`);
|
|
17587
|
+
}
|
|
17588
|
+
let raw;
|
|
17589
|
+
try {
|
|
17590
|
+
raw = parseWire(text, { maxBytes: 16384 });
|
|
17591
|
+
} catch {
|
|
17592
|
+
return fail(`the relay's realm document ${url} is not JSON`);
|
|
17593
|
+
}
|
|
17594
|
+
const { realm: _id, ...published } = typeof raw === "object" && raw !== null ? raw : {};
|
|
17595
|
+
const parsed = DirectoryRealmConfig.safeParse(published);
|
|
17596
|
+
if (!parsed.success) fail(`the relay's realm document ${url} is not a directory realm`);
|
|
17597
|
+
const differs = realmMismatch(parsed.data, realm);
|
|
17598
|
+
if (differs.length > 0) fail(`the relay's realm document ${url} names another ${differs.join(", ")} than the environment: the home is not pinned`);
|
|
17599
|
+
return parsed.data.orgDomains !== void 0 ? {
|
|
17600
|
+
...realm,
|
|
17601
|
+
orgDomains: parsed.data.orgDomains
|
|
17602
|
+
} : realm;
|
|
17603
|
+
}
|
|
17604
|
+
var JELLY_ENV, JELLY_RESOURCE_NAME, ResourceEntry, Resources, wellKnownGet;
|
|
17605
|
+
var init_jelly_env = __esmMin((() => {
|
|
17606
|
+
init_dist$3();
|
|
17607
|
+
init_zod();
|
|
17608
|
+
init_jelly$1();
|
|
17609
|
+
JELLY_ENV = [
|
|
17610
|
+
"JELLY_ACTION_KEY",
|
|
17611
|
+
"JELLY_ID_ISSUER",
|
|
17612
|
+
"JELLY_RESOURCES",
|
|
17613
|
+
"JELLY_SERVICE_ANCHOR",
|
|
17614
|
+
"JELLY_DEVICE_LABEL",
|
|
17615
|
+
"JELLY_SOCKET_GROUP"
|
|
17616
|
+
];
|
|
17617
|
+
JELLY_RESOURCE_NAME = "messaging";
|
|
17618
|
+
ResourceEntry = looseObject({
|
|
17619
|
+
name: string$1().max(200),
|
|
17620
|
+
resource: string$1().min(1).max(2048),
|
|
17621
|
+
url: string$1().min(1).max(2048)
|
|
17622
|
+
});
|
|
17623
|
+
Resources = array(unknown()).max(64);
|
|
17624
|
+
wellKnownGet = async (url) => {
|
|
17625
|
+
const response = await fetch(url, {
|
|
17626
|
+
redirect: "error",
|
|
17627
|
+
signal: AbortSignal.timeout(5e3)
|
|
17628
|
+
});
|
|
17629
|
+
if (!response.ok) throw new MessagingError("relay_unavailable", `the relay answered ${response.status}`);
|
|
17630
|
+
const text = await response.text();
|
|
17631
|
+
if (Buffer.byteLength(text) > 16384) throw new MessagingError("too_large", "the relay's realm document is larger than 16 KiB");
|
|
17632
|
+
return text;
|
|
17633
|
+
};
|
|
17634
|
+
}));
|
|
17635
|
+
// packages/daemon/dist/consent/answer-preview.js
|
|
17636
|
+
function who(from) {
|
|
17637
|
+
const name = cleanPeerText(from.name, {
|
|
17638
|
+
singleLine: true,
|
|
17639
|
+
maxChars: 80
|
|
17640
|
+
}) || "someone";
|
|
17641
|
+
const email = from.email === void 0 ? void 0 : cleanPeerText(from.email, {
|
|
17642
|
+
singleLine: true,
|
|
17643
|
+
maxChars: 100
|
|
17644
|
+
});
|
|
17645
|
+
return `${name} (${email !== void 0 && email !== "" ? `${email}, ` : ""}${ORG[from.org]})`;
|
|
17646
|
+
}
|
|
17647
|
+
function answersFor(kind) {
|
|
17648
|
+
return kind === "suggestion" ? ["decline"] : kind === "contact" ? [
|
|
17649
|
+
"accept",
|
|
17650
|
+
"decline",
|
|
17651
|
+
"block"
|
|
17652
|
+
] : [
|
|
17653
|
+
"once",
|
|
17654
|
+
"always",
|
|
17655
|
+
"decline",
|
|
17656
|
+
"block"
|
|
17657
|
+
];
|
|
17658
|
+
}
|
|
17659
|
+
function answerSentence(input) {
|
|
17660
|
+
const { kind, answer } = input;
|
|
17661
|
+
if (answer !== void 0 && !answersFor(kind).includes(answer)) throw new MessagingError("invalid_request", `a ${kind === "message" || kind === "task" ? "request to your agent" : kind === "contact" ? "message request" : "suggestion"} takes ${answersFor(kind).join(", ")}`);
|
|
17662
|
+
const sender = who(input.from);
|
|
17663
|
+
const single = input.count <= 1;
|
|
17664
|
+
if (kind === "suggestion") {
|
|
17665
|
+
const email = cleanPeerText(input.email ?? "", {
|
|
17666
|
+
singleLine: true,
|
|
17667
|
+
maxChars: 100
|
|
17668
|
+
});
|
|
17669
|
+
if (answer === void 0) return `Your agent ${input.agent} suggests you send a message request to ${email}.`;
|
|
17670
|
+
return `Dismiss your agent ${input.agent}'s suggestion to contact ${email}. Nothing is sent.`;
|
|
17671
|
+
}
|
|
17672
|
+
if (kind === "contact") {
|
|
17673
|
+
const thing = single ? "their message request" : `their ${input.count} message requests`;
|
|
17674
|
+
if (answer === void 0) return `${sender} asks to message you.`;
|
|
17675
|
+
if (answer === "accept") return `${sender} may message you from now on: ${single ? "their request becomes their first message" : "their requests become their first messages"}, and they get your card.`;
|
|
17676
|
+
if (answer === "decline") return `Decline ${thing} from ${sender}. They are told it was declined.`;
|
|
17677
|
+
return `Block ${sender}: nothing from them or their agents reaches you, and ${thing} ${single ? "is" : "are"} declined.`;
|
|
17678
|
+
}
|
|
17679
|
+
const thing = single ? `this ${kind}` : `these ${input.count} requests`;
|
|
17680
|
+
const src = single ? `${sender}'s agent ${input.agent}` : `${sender}'s agents`;
|
|
17681
|
+
const dst = single ? `your agent ${input.to}` : "your agents";
|
|
17682
|
+
if (answer === void 0) return `${src} asks to send ${kind === "task" ? "a task" : "a message"} to ${dst}.`;
|
|
17683
|
+
if (answer === "once") return `Deliver ${thing} from ${src} to ${dst} this once, guarded: ${single ? "your agent" : "your agents"} may answer only them.`;
|
|
17684
|
+
if (answer === "always") return `Deliver ${thing} from ${src} to ${dst}, and let their agents ask yours from now on without asking you.`;
|
|
17685
|
+
if (answer === "decline") return `Decline ${thing} from ${src}. They are told it was declined.`;
|
|
17686
|
+
return `Block ${sender}: nothing from them or their agents reaches you, and ${thing} ${single ? "is" : "are"} declined.`;
|
|
17687
|
+
}
|
|
17688
|
+
var ORG;
|
|
17689
|
+
var init_answer_preview = __esmMin((() => {
|
|
17690
|
+
init_dist$3();
|
|
17691
|
+
ORG = {
|
|
17692
|
+
same: "your org",
|
|
17693
|
+
other: "another org",
|
|
17694
|
+
none: "no org"
|
|
17695
|
+
};
|
|
17696
|
+
}));
|
|
17697
|
+
// packages/daemon/dist/snapshot.js
|
|
17698
|
+
function sha256File(path) {
|
|
17699
|
+
const data = readFileSync(path);
|
|
17700
|
+
return {
|
|
17701
|
+
bytes: data.length,
|
|
17702
|
+
sha256: createHash("sha256").update(data).digest("hex")
|
|
17703
|
+
};
|
|
17704
|
+
}
|
|
17705
|
+
var SNAPSHOT_HOLD_MS, Snapshots;
|
|
17706
|
+
var init_snapshot = __esmMin((() => {
|
|
17707
|
+
init_dist$3();
|
|
17708
|
+
init_dist$2();
|
|
17709
|
+
SNAPSHOT_HOLD_MS = 36e5;
|
|
17710
|
+
Snapshots = class {
|
|
17711
|
+
#deps;
|
|
17712
|
+
#timer;
|
|
17713
|
+
constructor(deps) {
|
|
17714
|
+
this.#deps = deps;
|
|
17715
|
+
}
|
|
17716
|
+
get folder() {
|
|
17717
|
+
return join(this.#deps.run, "snapshot");
|
|
17718
|
+
}
|
|
17719
|
+
take() {
|
|
17720
|
+
const folder = this.folder;
|
|
17721
|
+
if (existsSync(folder)) throw new MessagingError("invalid_state", "a snapshot is held: release it with daemon.snapshot {done: true} first");
|
|
17722
|
+
const group = this.#deps.group;
|
|
17723
|
+
mkdirSync(folder, { mode: group === void 0 ? 448 : 488 });
|
|
17724
|
+
try {
|
|
17725
|
+
if (!IS_WINDOWS && group !== void 0) {
|
|
17726
|
+
chownSync(folder, process.geteuid?.() ?? -1, group);
|
|
17727
|
+
chmodSync(folder, 488);
|
|
17728
|
+
}
|
|
17729
|
+
const fileMode = group === void 0 ? 384 : 416;
|
|
17730
|
+
const files = [];
|
|
17731
|
+
const db = join(folder, "messaging.db");
|
|
17732
|
+
this.#deps.vacuumInto(db);
|
|
17733
|
+
if (existsSync(this.#deps.config)) writeFileSync(join(folder, "config.json"), readFileSync(this.#deps.config), { mode: fileMode });
|
|
17734
|
+
for (const name of ["messaging.db", "config.json"]) {
|
|
17735
|
+
const path = join(folder, name);
|
|
17736
|
+
if (!existsSync(path)) continue;
|
|
17737
|
+
if (!IS_WINDOWS) {
|
|
17738
|
+
if (group !== void 0) chownSync(path, process.geteuid?.() ?? -1, group);
|
|
17739
|
+
chmodSync(path, fileMode);
|
|
17740
|
+
}
|
|
17741
|
+
files.push({
|
|
17742
|
+
name,
|
|
17743
|
+
...sha256File(path)
|
|
17744
|
+
});
|
|
17745
|
+
}
|
|
17746
|
+
const at = this.#deps.now();
|
|
17747
|
+
const until = at + SNAPSHOT_HOLD_MS;
|
|
17748
|
+
const manifest = {
|
|
17749
|
+
storeSchema: this.#deps.storeSchema,
|
|
17750
|
+
at,
|
|
17751
|
+
files
|
|
17752
|
+
};
|
|
17753
|
+
writeFileSync(join(folder, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, { mode: fileMode });
|
|
17754
|
+
if (!IS_WINDOWS && group !== void 0) chownSync(join(folder, "manifest.json"), process.geteuid?.() ?? -1, group);
|
|
17755
|
+
clearTimeout(this.#timer);
|
|
17756
|
+
this.#timer = setTimeout(() => this.release(), SNAPSHOT_HOLD_MS);
|
|
17757
|
+
this.#timer.unref();
|
|
17758
|
+
return {
|
|
17759
|
+
folder,
|
|
17760
|
+
files,
|
|
17761
|
+
storeSchema: this.#deps.storeSchema,
|
|
17762
|
+
at,
|
|
17763
|
+
until
|
|
17764
|
+
};
|
|
17765
|
+
} catch (error) {
|
|
17766
|
+
this.release();
|
|
17767
|
+
if (error instanceof MessagingError) throw error;
|
|
17768
|
+
throw new MessagingError("internal", `the snapshot failed: ${error.code ?? "error"}`);
|
|
17769
|
+
}
|
|
17770
|
+
}
|
|
17771
|
+
release() {
|
|
17772
|
+
clearTimeout(this.#timer);
|
|
17773
|
+
this.#timer = void 0;
|
|
17774
|
+
let present;
|
|
17775
|
+
try {
|
|
17776
|
+
lstatSync(this.folder);
|
|
17777
|
+
present = true;
|
|
17778
|
+
} catch {
|
|
17779
|
+
present = false;
|
|
17780
|
+
}
|
|
17781
|
+
if (present) rmSync(this.folder, {
|
|
17782
|
+
recursive: true,
|
|
17783
|
+
force: true
|
|
17784
|
+
});
|
|
17785
|
+
return { released: present };
|
|
17786
|
+
}
|
|
17787
|
+
close() {
|
|
17788
|
+
this.release();
|
|
17789
|
+
}
|
|
17790
|
+
};
|
|
17791
|
+
}));
|
|
17297
17792
|
// packages/daemon/dist/notices.js
|
|
17298
17793
|
function firstLine(text) {
|
|
17299
17794
|
const first = cleanPeerText(text.split(/\r\n|\n|\r/, 1)[0] ?? "", { singleLine: true });
|
|
@@ -18298,7 +18793,7 @@ function taskRow(r) {
|
|
|
18298
18793
|
return row;
|
|
18299
18794
|
}
|
|
18300
18795
|
var DatabaseSync$1, CHAT_COUNTER, MIGRATIONS, BASELINE, kvGet, kvSet, schemaNumber, TASKS, text$1, num, json, opt, Store, UNSPACED;
|
|
18301
|
-
var init_store$
|
|
18796
|
+
var init_store$2 = __esmMin((() => {
|
|
18302
18797
|
init_dist$3();
|
|
18303
18798
|
({DatabaseSync: DatabaseSync$1} = process.getBuiltinModule("node:sqlite"));
|
|
18304
18799
|
CHAT_COUNTER = "chat";
|
|
@@ -18601,6 +19096,10 @@ CREATE TABLE IF NOT EXISTS idempotency (
|
|
|
18601
19096
|
this.#statements.clear();
|
|
18602
19097
|
this.#db.close();
|
|
18603
19098
|
}
|
|
19099
|
+
vacuumInto(path) {
|
|
19100
|
+
if (this.#depth > 0) throw new MessagingError("invalid_state", "a snapshot is not taken inside a transaction");
|
|
19101
|
+
this.#db.exec(`VACUUM INTO '${path.replaceAll("'", "''")}'`);
|
|
19102
|
+
}
|
|
18604
19103
|
tx(fn) {
|
|
18605
19104
|
if (this.#depth > 0) {
|
|
18606
19105
|
const name = `sp${this.#depth}`;
|
|
@@ -19296,6 +19795,12 @@ CREATE TABLE IF NOT EXISTS idempotency (
|
|
|
19296
19795
|
lastAt: Number(row.at)
|
|
19297
19796
|
}));
|
|
19298
19797
|
}
|
|
19798
|
+
awaitedRequests(sessions, terminal, limit) {
|
|
19799
|
+
if (sessions.length === 0) return [];
|
|
19800
|
+
return this.#all(`SELECT request FROM tasks WHERE role = 'requester' AND request IS NOT NULL
|
|
19801
|
+
AND session IN (${sessions.map(() => "?").join(", ")}) AND state NOT IN (${terminal.map(() => "?").join(", ")})
|
|
19802
|
+
ORDER BY updated_at DESC, task_id DESC LIMIT ?`, ...sessions, ...terminal, limit).map((row) => String(row.request));
|
|
19803
|
+
}
|
|
19299
19804
|
task(taskId, role) {
|
|
19300
19805
|
const row = this.#get(`SELECT * FROM ${TASKS} WHERE task_id = ? AND role = ?`, taskId, role);
|
|
19301
19806
|
return row && taskRow(row);
|
|
@@ -20687,6 +21192,23 @@ function jellyConfig(path, jelly) {
|
|
|
20687
21192
|
if (config.deployment === "jelly") throw new MessagingError("invalid_request", "config.json says deployment jelly: start messagingd with --deployment jelly and the host's --act-key");
|
|
20688
21193
|
return;
|
|
20689
21194
|
}
|
|
21195
|
+
if (jelly.realm !== void 0) {
|
|
21196
|
+
if (config.realm === void 0) {
|
|
21197
|
+
if (config.relay !== void 0 || config.person !== void 0) throw new MessagingError("invalid_request", `${path} is a roster home (relay, person): a Jelly host's home is pinned to a directory realm; give the service a new STATE_DIRECTORY`);
|
|
21198
|
+
if (config.ipc?.agentGroup !== void 0) throw new MessagingError("invalid_request", "deployment jelly shares its sockets with --socket-group, not ipc.agentGroup");
|
|
21199
|
+
writeConfig(path, {
|
|
21200
|
+
...config,
|
|
21201
|
+
realm: jelly.realm,
|
|
21202
|
+
deployment: "jelly"
|
|
21203
|
+
});
|
|
21204
|
+
return;
|
|
21205
|
+
}
|
|
21206
|
+
const differs = realmMismatch(config.realm, jelly.realm);
|
|
21207
|
+
if (differs.length > 0) {
|
|
21208
|
+
const which = differs.map((member) => `${member} ${JSON.stringify(config.realm[member])} (the environment: ${JSON.stringify(jelly.realm[member])})`).join(", ");
|
|
21209
|
+
throw new MessagingError("invalid_request", `${path} pins another realm: ${which}. A home never changes realm, and the daemon does not rewrite it; restore the variables the home was made with, or give the service a new STATE_DIRECTORY`);
|
|
21210
|
+
}
|
|
21211
|
+
}
|
|
20690
21212
|
if (config.realm === void 0) throw new MessagingError("invalid_request", "deployment jelly needs a directory realm: config.json has no realm (a Jelly host supervises no roster realm)");
|
|
20691
21213
|
if (config.ipc?.agentGroup !== void 0) throw new MessagingError("invalid_request", "deployment jelly shares its sockets with --socket-group, not ipc.agentGroup");
|
|
20692
21214
|
if (config.deployment !== "jelly") writeConfig(path, {
|
|
@@ -20715,13 +21237,16 @@ var init_daemon = __esmMin((() => {
|
|
|
20715
21237
|
init_roster_identity();
|
|
20716
21238
|
init_directory_home();
|
|
20717
21239
|
init_jelly$1();
|
|
21240
|
+
init_jelly_env();
|
|
21241
|
+
init_answer_preview();
|
|
21242
|
+
init_snapshot();
|
|
20718
21243
|
init_notices();
|
|
20719
21244
|
init_counterpart();
|
|
20720
21245
|
init_resolve();
|
|
20721
21246
|
init_secrets();
|
|
20722
21247
|
init_roster_sync();
|
|
20723
21248
|
init_storage_errors();
|
|
20724
|
-
init_store$
|
|
21249
|
+
init_store$2();
|
|
20725
21250
|
init_tasks();
|
|
20726
21251
|
init_version();
|
|
20727
21252
|
init_wake_bridge();
|
|
@@ -20776,6 +21301,8 @@ var init_daemon = __esmMin((() => {
|
|
|
20776
21301
|
#closed = false;
|
|
20777
21302
|
#online = new Set();
|
|
20778
21303
|
#holders = new Map();
|
|
21304
|
+
#ticketEnds = new Map();
|
|
21305
|
+
#lapsed = new Map();
|
|
20779
21306
|
#policy = {
|
|
20780
21307
|
rate: inboundRateLimiter(),
|
|
20781
21308
|
presence: responseRateLimiter(),
|
|
@@ -20795,6 +21322,9 @@ var init_daemon = __esmMin((() => {
|
|
|
20795
21322
|
#requests;
|
|
20796
21323
|
#notices;
|
|
20797
21324
|
#jellyHost;
|
|
21325
|
+
#attached = false;
|
|
21326
|
+
#wakeArmed = false;
|
|
21327
|
+
#snapshots;
|
|
20798
21328
|
#jellyAuth;
|
|
20799
21329
|
#state = "starting";
|
|
20800
21330
|
#sync;
|
|
@@ -20908,6 +21438,14 @@ var init_daemon = __esmMin((() => {
|
|
|
20908
21438
|
address: (sender, session) => peerRef(sender, session).address,
|
|
20909
21439
|
held: (row) => this.#notices.held(row)
|
|
20910
21440
|
});
|
|
21441
|
+
this.#snapshots = new Snapshots({
|
|
21442
|
+
run: paths.run,
|
|
21443
|
+
config: paths.config,
|
|
21444
|
+
vacuumInto: (path) => this.#store.vacuumInto(path),
|
|
21445
|
+
storeSchema: 9,
|
|
21446
|
+
group: options.jellyHost?.socketGroup,
|
|
21447
|
+
now: this.#now
|
|
21448
|
+
});
|
|
20911
21449
|
this.#notices = new Notices({
|
|
20912
21450
|
store,
|
|
20913
21451
|
now: this.#now,
|
|
@@ -20947,6 +21485,14 @@ var init_daemon = __esmMin((() => {
|
|
|
20947
21485
|
static async start(options) {
|
|
20948
21486
|
const paths = homePaths(options.home, options.jellyHost?.run);
|
|
20949
21487
|
ensureHome(paths);
|
|
21488
|
+
const jelly = options.jellyHost;
|
|
21489
|
+
if (jelly?.realm !== void 0 && readConfig(paths.config).realm === void 0) options = {
|
|
21490
|
+
...options,
|
|
21491
|
+
jellyHost: {
|
|
21492
|
+
...jelly,
|
|
21493
|
+
realm: await pinRealm(jelly.realm, options.wellKnown)
|
|
21494
|
+
}
|
|
21495
|
+
};
|
|
20950
21496
|
jellyConfig(paths.config, options.jellyHost);
|
|
20951
21497
|
const lock = acquireLock(paths.lock);
|
|
20952
21498
|
let store;
|
|
@@ -20980,6 +21526,7 @@ var init_daemon = __esmMin((() => {
|
|
|
20980
21526
|
} else if (entries.length > 0) this.#store.clearRoster();
|
|
20981
21527
|
this.#sync.load();
|
|
20982
21528
|
this.#store.keepAllLive(this.#now(), "cli");
|
|
21529
|
+
this.#snapshots.release();
|
|
20983
21530
|
const handlers = this.#handlers();
|
|
20984
21531
|
const jelly = this.#jellyHost;
|
|
20985
21532
|
if (jelly) this.#jellyAuth = new JellyAuth({
|
|
@@ -21117,6 +21664,7 @@ var init_daemon = __esmMin((() => {
|
|
|
21117
21664
|
for (const server of this.#ipc) await server.close().catch(() => {});
|
|
21118
21665
|
await Promise.race([this.#flushing, new Promise((resolve) => setTimeout(resolve, 2e3).unref())]);
|
|
21119
21666
|
this.#store.close();
|
|
21667
|
+
this.#snapshots.close();
|
|
21120
21668
|
} finally {
|
|
21121
21669
|
this.#lock.release();
|
|
21122
21670
|
this.#log("info", "daemon.stopped", { pid: process.pid });
|
|
@@ -21289,6 +21837,7 @@ var init_daemon = __esmMin((() => {
|
|
|
21289
21837
|
},
|
|
21290
21838
|
"request.list": async () => ({ requests: [...this.#holds.list().requests, ...await this.#requests.list()] }),
|
|
21291
21839
|
"request.answer": (p) => this.#requests.holds(p.id) ? this.#requests.answer(p) : this.#holds.answer(p),
|
|
21840
|
+
"request.answer.preview": (p) => this.#answerPreview(p),
|
|
21292
21841
|
"contact.request": (p) => this.#requests.request(p),
|
|
21293
21842
|
"contact.request.preview": (p) => this.#requests.preview(p),
|
|
21294
21843
|
"card.get": async (p) => {
|
|
@@ -21297,6 +21846,11 @@ var init_daemon = __esmMin((() => {
|
|
|
21297
21846
|
return { sent: await this.#cards.get(p.person) };
|
|
21298
21847
|
},
|
|
21299
21848
|
"daemon.health": () => this.#health(),
|
|
21849
|
+
"daemon.woke": () => this.#woke(),
|
|
21850
|
+
"wake.arm": (p) => this.#wakeArm(p),
|
|
21851
|
+
"wake.disarm": () => this.#wakeDisarm(),
|
|
21852
|
+
"daemon.snapshot": (p) => p.done ? this.#snapshots.release() : this.#snapshots.take(),
|
|
21853
|
+
"person.attached": (p) => this.#personAttached(p.attached),
|
|
21300
21854
|
"agents.list": () => this.#agentsList(),
|
|
21301
21855
|
"agents.read": (p) => this.#agentsRead(p),
|
|
21302
21856
|
"notice.subscribe": (p, ctx, authority) => this.#notices.subscribe(ctx, p, authority?.scope === "host" ? authority.expiresAt : void 0),
|
|
@@ -21498,6 +22052,11 @@ var init_daemon = __esmMin((() => {
|
|
|
21498
22052
|
let holders = this.#holders.get(p.key);
|
|
21499
22053
|
if (!holders) this.#holders.set(p.key, holders = new Set());
|
|
21500
22054
|
holders.add(ctx.id);
|
|
22055
|
+
if (ticket) {
|
|
22056
|
+
this.#ticketEnds.set(p.key, Math.max(this.#ticketEnds.get(p.key) ?? 0, ticket.exp * 1e3));
|
|
22057
|
+
for (const id of this.#lapsed.get(p.key) ?? []) holders.add(id);
|
|
22058
|
+
this.#lapsed.delete(p.key);
|
|
22059
|
+
}
|
|
21501
22060
|
this.#sessionsChanged();
|
|
21502
22061
|
return {
|
|
21503
22062
|
session: name,
|
|
@@ -21505,9 +22064,23 @@ var init_daemon = __esmMin((() => {
|
|
|
21505
22064
|
};
|
|
21506
22065
|
}
|
|
21507
22066
|
#bound(p, ctx) {
|
|
22067
|
+
const ends = this.#ticketEnds.get(p.key);
|
|
22068
|
+
const held = this.#holders.get(p.key)?.has(ctx.id) === true || this.#lapsed.get(p.key)?.has(ctx.id) === true;
|
|
22069
|
+
if (ends !== void 0 && ends <= this.#now() && held) throw new MessagingError("ticket_invalid", "the session ticket this session registered with has expired: register again with a fresh ticket from the host");
|
|
21508
22070
|
if (!this.#holders.get(p.key)?.has(ctx.id)) throw new MessagingError("not_found", "this session key is not registered on this connection");
|
|
21509
22071
|
return p;
|
|
21510
22072
|
}
|
|
22073
|
+
#expireTickets(now) {
|
|
22074
|
+
let changed = false;
|
|
22075
|
+
for (const [key, ends] of this.#ticketEnds) {
|
|
22076
|
+
if (ends > now || !this.#holders.has(key)) continue;
|
|
22077
|
+
this.#lapsed.set(key, this.#holders.get(key));
|
|
22078
|
+
this.#holders.delete(key);
|
|
22079
|
+
this.#log("info", "session.ticket.expired", {});
|
|
22080
|
+
if (this.#store.keepSession(key, now)) changed = true;
|
|
22081
|
+
}
|
|
22082
|
+
if (changed) this.#sessionsChanged();
|
|
22083
|
+
}
|
|
21511
22084
|
#sessionOf(key) {
|
|
21512
22085
|
const session = this.#store.sessionByKey(key);
|
|
21513
22086
|
if (!session) throw new MessagingError("not_found", "this session key is not registered");
|
|
@@ -21528,6 +22101,8 @@ var init_daemon = __esmMin((() => {
|
|
|
21528
22101
|
}
|
|
21529
22102
|
#unregister(p) {
|
|
21530
22103
|
this.#holders.delete(p.key);
|
|
22104
|
+
this.#ticketEnds.delete(p.key);
|
|
22105
|
+
this.#lapsed.delete(p.key);
|
|
21531
22106
|
if (this.#store.keepSession(p.key, this.#now())) this.#sessionsChanged();
|
|
21532
22107
|
return { ok: true };
|
|
21533
22108
|
}
|
|
@@ -21536,9 +22111,11 @@ var init_daemon = __esmMin((() => {
|
|
|
21536
22111
|
this.#chats.disconnected(ctx);
|
|
21537
22112
|
this.#notices.disconnected(ctx.id);
|
|
21538
22113
|
let changed = false;
|
|
22114
|
+
for (const [key, lapsed] of [...this.#lapsed]) if (lapsed.delete(ctx.id) && lapsed.size === 0) this.#lapsed.delete(key);
|
|
21539
22115
|
for (const [key, holders] of [...this.#holders]) {
|
|
21540
22116
|
if (!holders.delete(ctx.id) || holders.size > 0) continue;
|
|
21541
22117
|
this.#holders.delete(key);
|
|
22118
|
+
this.#ticketEnds.delete(key);
|
|
21542
22119
|
if (this.#store.sessionByKey(key)?.agent === "cli") continue;
|
|
21543
22120
|
if (this.#store.keepSession(key, this.#now())) changed = true;
|
|
21544
22121
|
}
|
|
@@ -21586,7 +22163,8 @@ var init_daemon = __esmMin((() => {
|
|
|
21586
22163
|
unregisteredAt: s.unregisteredAt ?? this.#now()
|
|
21587
22164
|
})) : this.#store.sessions(), me.label, this.#store.next("presence"), this.#transport?.advertise?.(), {
|
|
21588
22165
|
wake: (s) => this.#wakes(s),
|
|
21589
|
-
share: new Set(this.#currentConfig().presence?.share ?? [])
|
|
22166
|
+
share: new Set(this.#currentConfig().presence?.share ?? []),
|
|
22167
|
+
away: this.#away()
|
|
21590
22168
|
}),
|
|
21591
22169
|
now: this.#now()
|
|
21592
22170
|
});
|
|
@@ -21947,10 +22525,17 @@ var init_daemon = __esmMin((() => {
|
|
|
21947
22525
|
async #peopleGet(p) {
|
|
21948
22526
|
const { realm, identity } = this.#joined();
|
|
21949
22527
|
const profile = await realm.profile(p.person);
|
|
22528
|
+
const fresh = this.#now() - (realm.kind === "directory" ? this.#tuning.presenceOnlineMs : this.#tuning.presenceMaxMs * 2);
|
|
22529
|
+
const away = (machine) => {
|
|
22530
|
+
if (machine === identity.machine) return this.#away();
|
|
22531
|
+
const row = this.#store.directoryRow(machine);
|
|
22532
|
+
return row !== void 0 && row.receivedAt >= fresh && row.presence.away === true;
|
|
22533
|
+
};
|
|
21950
22534
|
const devices = realm.known(p.person).devices.map((device) => ({
|
|
21951
22535
|
device: device.machine,
|
|
21952
22536
|
label: device.label,
|
|
21953
|
-
self: device.machine === identity.machine
|
|
22537
|
+
self: device.machine === identity.machine,
|
|
22538
|
+
...away(device.machine) ? { away: true } : {}
|
|
21954
22539
|
}));
|
|
21955
22540
|
return {
|
|
21956
22541
|
person: p.person,
|
|
@@ -22879,6 +23464,7 @@ var init_daemon = __esmMin((() => {
|
|
|
22879
23464
|
if (this.#closed) return;
|
|
22880
23465
|
this.#directory?.sweep();
|
|
22881
23466
|
const now = this.#now();
|
|
23467
|
+
this.#expireTickets(now);
|
|
22882
23468
|
let sessions = 0;
|
|
22883
23469
|
try {
|
|
22884
23470
|
this.#tx(() => {
|
|
@@ -23149,6 +23735,135 @@ var init_daemon = __esmMin((() => {
|
|
|
23149
23735
|
self: d.machine === identity.machine
|
|
23150
23736
|
})) };
|
|
23151
23737
|
}
|
|
23738
|
+
#away() {
|
|
23739
|
+
return this.#jellyHost !== void 0 && !this.#attached;
|
|
23740
|
+
}
|
|
23741
|
+
#personAttached(attached) {
|
|
23742
|
+
if (attached !== this.#attached) {
|
|
23743
|
+
this.#attached = attached;
|
|
23744
|
+
this.#log("info", "person.attached", { attached });
|
|
23745
|
+
this.#presenceSoon();
|
|
23746
|
+
}
|
|
23747
|
+
return { ok: true };
|
|
23748
|
+
}
|
|
23749
|
+
#woke() {
|
|
23750
|
+
this.#log("info", "daemon.woke", {});
|
|
23751
|
+
this.#transport?.retryNow?.();
|
|
23752
|
+
this.sweep();
|
|
23753
|
+
this.#presenceSoon();
|
|
23754
|
+
this.#wokeHook();
|
|
23755
|
+
return { ok: true };
|
|
23756
|
+
}
|
|
23757
|
+
#wokeHook() {
|
|
23758
|
+
if (!this.#wakeArmed) return;
|
|
23759
|
+
this.#wakeDisarm().catch((error) => this.#log("warn", "wake.disarm.failed", { code: codeOf$2(error) }));
|
|
23760
|
+
}
|
|
23761
|
+
async #wakeArm(p) {
|
|
23762
|
+
const home = this.#directory;
|
|
23763
|
+
if (!home) throw new MessagingError("invalid_state", "wake tickets are for a directory realm");
|
|
23764
|
+
if (home.person() === void 0) throw new MessagingError("invalid_state", "this device is not bound yet");
|
|
23765
|
+
const issuer = this.#config.realm?.issuer;
|
|
23766
|
+
if (issuer === void 0 || new URL(p.url).origin !== new URL(issuer).origin) throw new MessagingError("invalid_request", "the wake URL is not on the realm's Jelly ID");
|
|
23767
|
+
const agents = [...new Set(p.agents)];
|
|
23768
|
+
const awaiting = this.#store.awaitedRequests(agents, TERMINAL_STATES, 500);
|
|
23769
|
+
const sent = await home.wake({
|
|
23770
|
+
url: p.url,
|
|
23771
|
+
ticket: p.ticket,
|
|
23772
|
+
agents,
|
|
23773
|
+
awaiting
|
|
23774
|
+
});
|
|
23775
|
+
this.#wakeArmed = true;
|
|
23776
|
+
this.#log("info", "wake.arm", {
|
|
23777
|
+
relay: sent === "sent" ? "armed" : "pending",
|
|
23778
|
+
agents: agents.length,
|
|
23779
|
+
awaiting: awaiting.length
|
|
23780
|
+
});
|
|
23781
|
+
return {
|
|
23782
|
+
relay: sent === "sent" ? "armed" : "pending",
|
|
23783
|
+
awaiting: awaiting.length
|
|
23784
|
+
};
|
|
23785
|
+
}
|
|
23786
|
+
async #wakeDisarm() {
|
|
23787
|
+
const home = this.#directory;
|
|
23788
|
+
if (!home) throw new MessagingError("invalid_state", "wake tickets are for a directory realm");
|
|
23789
|
+
const sent = await home.wake("disarm");
|
|
23790
|
+
this.#wakeArmed = false;
|
|
23791
|
+
this.#log("info", "wake.disarm", { relay: sent === "sent" ? "disarmed" : "pending" });
|
|
23792
|
+
return { relay: sent === "sent" ? "disarmed" : "pending" };
|
|
23793
|
+
}
|
|
23794
|
+
async #answerPreview(p) {
|
|
23795
|
+
const { realm, me } = this.#joined();
|
|
23796
|
+
const now = this.#now();
|
|
23797
|
+
const profile = async (person) => {
|
|
23798
|
+
const found = await realm.profile(person).catch(() => void 0);
|
|
23799
|
+
return found ? {
|
|
23800
|
+
name: found.name.slice(0, 200),
|
|
23801
|
+
...found.email !== void 0 ? { email: found.email.slice(0, 320) } : {},
|
|
23802
|
+
org: found.org
|
|
23803
|
+
} : {
|
|
23804
|
+
name: String(person).slice(0, 200),
|
|
23805
|
+
org: "none"
|
|
23806
|
+
};
|
|
23807
|
+
};
|
|
23808
|
+
const answer = p.answer;
|
|
23809
|
+
const ask = this.#store.held(p.id);
|
|
23810
|
+
if (ask && ask.exp > now) {
|
|
23811
|
+
const kind = ask.payload.kind;
|
|
23812
|
+
const count = p.all ? this.#store.helds(ask.person).filter((row) => row.exp > now).length : 1;
|
|
23813
|
+
const from = await profile(ask.person);
|
|
23814
|
+
const agent = ask.envelope.from.agent ?? "";
|
|
23815
|
+
return {
|
|
23816
|
+
kind,
|
|
23817
|
+
from,
|
|
23818
|
+
...agent !== "" ? { agent } : {},
|
|
23819
|
+
to: ask.session,
|
|
23820
|
+
count,
|
|
23821
|
+
sentence: answerSentence({
|
|
23822
|
+
kind,
|
|
23823
|
+
from,
|
|
23824
|
+
agent,
|
|
23825
|
+
to: ask.session,
|
|
23826
|
+
answer,
|
|
23827
|
+
count
|
|
23828
|
+
})
|
|
23829
|
+
};
|
|
23830
|
+
}
|
|
23831
|
+
const request = this.#store.contactRequest(p.id);
|
|
23832
|
+
if (request && request.exp > now) {
|
|
23833
|
+
const count = p.all ? this.#store.contactRequests(request.person).filter((row) => row.exp > now).length : 1;
|
|
23834
|
+
const from = await profile(request.person);
|
|
23835
|
+
return {
|
|
23836
|
+
kind: "contact",
|
|
23837
|
+
from,
|
|
23838
|
+
count,
|
|
23839
|
+
sentence: answerSentence({
|
|
23840
|
+
kind: "contact",
|
|
23841
|
+
from,
|
|
23842
|
+
answer,
|
|
23843
|
+
count
|
|
23844
|
+
})
|
|
23845
|
+
};
|
|
23846
|
+
}
|
|
23847
|
+
const suggestion = this.#store.contactSuggestion(p.id);
|
|
23848
|
+
if (suggestion) {
|
|
23849
|
+
const from = await profile(me.person);
|
|
23850
|
+
return {
|
|
23851
|
+
kind: "suggestion",
|
|
23852
|
+
from,
|
|
23853
|
+
agent: suggestion.session,
|
|
23854
|
+
count: 1,
|
|
23855
|
+
sentence: answerSentence({
|
|
23856
|
+
kind: "suggestion",
|
|
23857
|
+
from,
|
|
23858
|
+
agent: suggestion.session,
|
|
23859
|
+
email: suggestion.email,
|
|
23860
|
+
answer,
|
|
23861
|
+
count: 1
|
|
23862
|
+
})
|
|
23863
|
+
};
|
|
23864
|
+
}
|
|
23865
|
+
throw new MessagingError("not_found", "no request with that id is held here");
|
|
23866
|
+
}
|
|
23152
23867
|
#me() {
|
|
23153
23868
|
return (this.#realm ?? this.#directory?.realm)?.self()?.person;
|
|
23154
23869
|
}
|
|
@@ -26709,6 +27424,9 @@ var init_direct_transport = __esmMin((() => {
|
|
|
26709
27424
|
presence(env) {
|
|
26710
27425
|
this.#relay.presence(env);
|
|
26711
27426
|
}
|
|
27427
|
+
retryNow() {
|
|
27428
|
+
this.#relay.retryNow?.();
|
|
27429
|
+
}
|
|
26712
27430
|
appendRoster(entry) {
|
|
26713
27431
|
return this.#relay.appendRoster(entry);
|
|
26714
27432
|
}
|
|
@@ -26767,7 +27485,7 @@ var init_pages = __esmMin((() => {
|
|
|
26767
27485
|
}));
|
|
26768
27486
|
// packages/relay/dist/store.js
|
|
26769
27487
|
var DatabaseSync;
|
|
26770
|
-
var init_store = __esmMin((() => {
|
|
27488
|
+
var init_store$1 = __esmMin((() => {
|
|
26771
27489
|
init_dist$3();
|
|
26772
27490
|
({DatabaseSync} = process.getBuiltinModule("node:sqlite"));
|
|
26773
27491
|
}));
|
|
@@ -26789,6 +27507,10 @@ var init_feed = __esmMin((() => {
|
|
|
26789
27507
|
var init_pacing = __esmMin((() => {
|
|
26790
27508
|
init_dist$3();
|
|
26791
27509
|
}));
|
|
27510
|
+
// packages/relay/dist/directory/wakes.js
|
|
27511
|
+
var init_wakes = __esmMin((() => {
|
|
27512
|
+
init_dist$3();
|
|
27513
|
+
}));
|
|
26792
27514
|
// packages/relay/dist/directory/module.js
|
|
26793
27515
|
var init_module = __esmMin((() => {
|
|
26794
27516
|
init_dist$3();
|
|
@@ -26796,6 +27518,11 @@ var init_module = __esmMin((() => {
|
|
|
26796
27518
|
init_feed();
|
|
26797
27519
|
init_jelly();
|
|
26798
27520
|
init_pacing();
|
|
27521
|
+
init_wakes();
|
|
27522
|
+
}));
|
|
27523
|
+
// packages/relay/dist/directory/store.js
|
|
27524
|
+
var init_store = __esmMin((() => {
|
|
27525
|
+
init_dist$3();
|
|
26799
27526
|
}));
|
|
26800
27527
|
// packages/relay/dist/server.js
|
|
26801
27528
|
var init_server = __esmMin((() => {
|
|
@@ -26804,8 +27531,9 @@ var init_server = __esmMin((() => {
|
|
|
26804
27531
|
init_frames();
|
|
26805
27532
|
init_limits();
|
|
26806
27533
|
init_pages();
|
|
26807
|
-
init_store();
|
|
27534
|
+
init_store$1();
|
|
26808
27535
|
init_module();
|
|
27536
|
+
init_store();
|
|
26809
27537
|
}));
|
|
26810
27538
|
// packages/relay/dist/backoff.js
|
|
26811
27539
|
function backoffDelay(attempt, minMs, maxMs, random = Math.random) {
|
|
@@ -27170,6 +27898,13 @@ var init_client = __esmMin((() => {
|
|
|
27170
27898
|
this.#attempt = 0;
|
|
27171
27899
|
if (!this.#ws && !this.#retryTimer) this.#open();
|
|
27172
27900
|
}
|
|
27901
|
+
retryNow() {
|
|
27902
|
+
if (!this.#running || this.#ws || !this.#retryTimer) return;
|
|
27903
|
+
clearTimeout(this.#retryTimer);
|
|
27904
|
+
this.#retryTimer = void 0;
|
|
27905
|
+
this.#attempt = 0;
|
|
27906
|
+
this.#open();
|
|
27907
|
+
}
|
|
27173
27908
|
async stop() {
|
|
27174
27909
|
this.#running = false;
|
|
27175
27910
|
clearTimeout(this.#retryTimer);
|
|
@@ -27276,6 +28011,18 @@ var init_client = __esmMin((() => {
|
|
|
27276
28011
|
persons
|
|
27277
28012
|
});
|
|
27278
28013
|
}
|
|
28014
|
+
wakeArm(arm) {
|
|
28015
|
+
return this.#dir("dir.ok", {
|
|
28016
|
+
t: "wake.arm",
|
|
28017
|
+
url: arm.url,
|
|
28018
|
+
ticket: arm.ticket,
|
|
28019
|
+
agents: arm.agents,
|
|
28020
|
+
awaiting: arm.awaiting
|
|
28021
|
+
});
|
|
28022
|
+
}
|
|
28023
|
+
wakeDisarm() {
|
|
28024
|
+
return this.#dir("dir.ok", { t: "wake.disarm" });
|
|
28025
|
+
}
|
|
27279
28026
|
dirErase(entries, token) {
|
|
27280
28027
|
return this.#dir("dir.ok", {
|
|
27281
28028
|
t: "dir.erase",
|
|
@@ -27613,6 +28360,7 @@ var init_relay_transport = __esmMin((() => {
|
|
|
27613
28360
|
#running = false;
|
|
27614
28361
|
#watched = [];
|
|
27615
28362
|
#presence = [];
|
|
28363
|
+
#wake;
|
|
27616
28364
|
#opening = new Set();
|
|
27617
28365
|
constructor(options) {
|
|
27618
28366
|
this.#options = options;
|
|
@@ -27649,6 +28397,7 @@ var init_relay_transport = __esmMin((() => {
|
|
|
27649
28397
|
for (const done of [...this.#opening]) done();
|
|
27650
28398
|
if (this.#watched.length > 0) client.dirWatch(this.#watched).catch(() => {});
|
|
27651
28399
|
if (this.#presence.length > 0) client.presenceWatch(this.#presence).catch(() => {});
|
|
28400
|
+
if (this.#wake !== void 0) this.#sendWake(client, this.#wake).catch(() => {});
|
|
27652
28401
|
const sth = "sth" in state ? STH.safeParse(state.sth) : void 0;
|
|
27653
28402
|
pushes.state(true, sth?.success ? sth.data : void 0);
|
|
27654
28403
|
},
|
|
@@ -27760,6 +28509,30 @@ var init_relay_transport = __esmMin((() => {
|
|
|
27760
28509
|
presence(env) {
|
|
27761
28510
|
this.#client?.presence(env);
|
|
27762
28511
|
}
|
|
28512
|
+
retryNow() {
|
|
28513
|
+
this.#client?.retryNow();
|
|
28514
|
+
}
|
|
28515
|
+
async wake(state) {
|
|
28516
|
+
this.#wake = state;
|
|
28517
|
+
const client = this.#connect();
|
|
28518
|
+
if (!client?.connected) return "pending";
|
|
28519
|
+
try {
|
|
28520
|
+
await this.#sendWake(client, state);
|
|
28521
|
+
return "sent";
|
|
28522
|
+
} catch (error) {
|
|
28523
|
+
if (isMessagingError(error) && error.code === "relay_unavailable") return "pending";
|
|
28524
|
+
throw error;
|
|
28525
|
+
}
|
|
28526
|
+
}
|
|
28527
|
+
async #sendWake(client, state) {
|
|
28528
|
+
try {
|
|
28529
|
+
parse(DirOkFrame, await (state === "disarm" ? client.wakeDisarm() : client.wakeArm(state)));
|
|
28530
|
+
} catch (error) {
|
|
28531
|
+
if (!(isMessagingError(error) && error.code === "relay_unavailable") && this.#wake === state) this.#wake = void 0;
|
|
28532
|
+
throw error;
|
|
28533
|
+
}
|
|
28534
|
+
if (this.#wake === state) this.#wake = void 0;
|
|
28535
|
+
}
|
|
27763
28536
|
async presenceWatch(persons) {
|
|
27764
28537
|
this.#presence = [...persons];
|
|
27765
28538
|
const client = this.#connect();
|
|
@@ -27777,6 +28550,7 @@ var dist_exports = __exportAll({
|
|
|
27777
28550
|
Config: () => Config,
|
|
27778
28551
|
DirectConfig: () => DirectConfig,
|
|
27779
28552
|
DirectTransport: () => DirectTransport,
|
|
28553
|
+
JELLY_ENV: () => JELLY_ENV,
|
|
27780
28554
|
LockHeldError: () => LockHeldError,
|
|
27781
28555
|
PROTOCOL: () => PROTOCOL,
|
|
27782
28556
|
RelayDirectoryLink: () => RelayDirectoryLink,
|
|
@@ -27785,12 +28559,15 @@ var dist_exports = __exportAll({
|
|
|
27785
28559
|
acquireLock: () => acquireLock,
|
|
27786
28560
|
createLogger: () => createLogger,
|
|
27787
28561
|
homePaths: () => homePaths,
|
|
28562
|
+
jellyEnvNamed: () => jellyEnvNamed,
|
|
27788
28563
|
jellyLabel: () => jellyLabel,
|
|
27789
28564
|
readActKey: () => readActKey,
|
|
27790
28565
|
readConfig: () => readConfig,
|
|
28566
|
+
readJellyEnv: () => readJellyEnv,
|
|
27791
28567
|
relayDirectoryLink: () => relayDirectoryLink,
|
|
27792
28568
|
relayOps: () => relayOps,
|
|
27793
28569
|
relayTransport: () => relayTransport,
|
|
28570
|
+
relayUrlOf: () => relayUrlOf,
|
|
27794
28571
|
startDaemon: () => startDaemon,
|
|
27795
28572
|
withDirect: () => withDirect
|
|
27796
28573
|
});
|
|
@@ -27812,7 +28589,8 @@ var init_dist = __esmMin((() => {
|
|
|
27812
28589
|
init_log();
|
|
27813
28590
|
init_version();
|
|
27814
28591
|
init_jelly$1();
|
|
27815
|
-
|
|
28592
|
+
init_jelly_env();
|
|
28593
|
+
init_store$2();
|
|
27816
28594
|
init_dist$3();
|
|
27817
28595
|
}));
|
|
27818
28596
|
// packages/daemon/dist/bin/daemon-main.js
|
|
@@ -27827,7 +28605,13 @@ Runs the Messaging daemon in the foreground for one MESSAGING_HOME (default ~/.m
|
|
|
27827
28605
|
--version print the version, the store schema and the protocol (--json: as JSON); needs no home
|
|
27828
28606
|
--help print this help
|
|
27829
28607
|
|
|
27830
|
-
Under a Jelly host (spec §8.11):
|
|
28608
|
+
Under a Jelly host (spec §8.11), with no arguments: the environment start contract. JELLY_ACTION_KEY (the path of
|
|
28609
|
+
the host's public action key) starts it; STATE_DIRECTORY is the home, RUNTIME_DIRECTORY the socket folder,
|
|
28610
|
+
JELLY_DEVICE_LABEL the label, JELLY_SOCKET_GROUP the group, and JELLY_ID_ISSUER, JELLY_RESOURCES (the messaging entry)
|
|
28611
|
+
and JELLY_SERVICE_ANCHOR the realm, which the first start writes to config.json. Flags win: a start with
|
|
28612
|
+
--deployment jelly reads none of these; any other start with JELLY_* variables set is refused.
|
|
28613
|
+
|
|
28614
|
+
Or by flags:
|
|
27831
28615
|
--deployment jelly every human and admin call needs the host's action grant
|
|
27832
28616
|
--act-key <path> the host's public action key (an Ed25519 JWK); required
|
|
27833
28617
|
--label <label> this device's label, from the host; required
|
|
@@ -27884,7 +28668,16 @@ function versionText(json, info) {
|
|
|
27884
28668
|
if (json) return `${JSON.stringify(info)}\n`;
|
|
27885
28669
|
return `messagingd ${info.version} (store schema ${info.storeSchema}, protocol ${info.protocol})\n`;
|
|
27886
28670
|
}
|
|
27887
|
-
|
|
28671
|
+
function startSource(values, env, named) {
|
|
28672
|
+
if (Object.keys(values).length > 0) {
|
|
28673
|
+
if (values["--deployment"] === "jelly" || named.length === 0) return "flags";
|
|
28674
|
+
return `${named[0]} is set, so the environment starts a Jelly host, but the flags start a self-host daemon; use one or the other`;
|
|
28675
|
+
}
|
|
28676
|
+
if ((env.JELLY_ACTION_KEY ?? "") !== "") return "environment";
|
|
28677
|
+
if (named.length > 0) return `${named[0]} is set without JELLY_ACTION_KEY: a Jelly start needs the host's action key`;
|
|
28678
|
+
return "self";
|
|
28679
|
+
}
|
|
28680
|
+
async function daemonMain(argv, env = process.env) {
|
|
27888
28681
|
const args = parseArgs(argv);
|
|
27889
28682
|
if (typeof args === "string") {
|
|
27890
28683
|
process.stderr.write(`messagingd: ${args}\n\n${USAGE}`);
|
|
@@ -27894,7 +28687,7 @@ async function daemonMain(argv) {
|
|
|
27894
28687
|
process.stdout.write(USAGE);
|
|
27895
28688
|
return 0;
|
|
27896
28689
|
}
|
|
27897
|
-
const { startDaemon, homePaths, createLogger, readActKey, jellyLabel, VERSION, STORE_SCHEMA, PROTOCOL } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
28690
|
+
const { startDaemon, homePaths, createLogger, readActKey, jellyLabel, readJellyEnv, jellyEnvNamed, VERSION, STORE_SCHEMA, PROTOCOL } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
27898
28691
|
if (args.version) {
|
|
27899
28692
|
process.stdout.write(versionText(args.json === true, {
|
|
27900
28693
|
version: VERSION,
|
|
@@ -27904,7 +28697,19 @@ async function daemonMain(argv) {
|
|
|
27904
28697
|
return 0;
|
|
27905
28698
|
}
|
|
27906
28699
|
const { values } = args;
|
|
27907
|
-
const
|
|
28700
|
+
const source = startSource(values, env, jellyEnvNamed(env));
|
|
28701
|
+
if (source !== "flags" && source !== "environment" && source !== "self") {
|
|
28702
|
+
process.stderr.write(`messagingd: ${source}\n`);
|
|
28703
|
+
return 2;
|
|
28704
|
+
}
|
|
28705
|
+
let fromEnv;
|
|
28706
|
+
if (source === "environment") try {
|
|
28707
|
+
fromEnv = readJellyEnv(env);
|
|
28708
|
+
} catch (error) {
|
|
28709
|
+
process.stderr.write(`messagingd: ${error instanceof Error ? error.message : "the Jelly environment is invalid"}\n`);
|
|
28710
|
+
return 1;
|
|
28711
|
+
}
|
|
28712
|
+
const home = fromEnv ? resolve(fromEnv.home) : values["--home"] ? resolve(values["--home"]) : messagingHome();
|
|
27908
28713
|
const level = process.env.MESSAGING_LOG_LEVEL;
|
|
27909
28714
|
const log = createLogger({
|
|
27910
28715
|
file: homePaths(home).log,
|
|
@@ -27913,7 +28718,14 @@ async function daemonMain(argv) {
|
|
|
27913
28718
|
});
|
|
27914
28719
|
let daemon;
|
|
27915
28720
|
try {
|
|
27916
|
-
const jellyHost =
|
|
28721
|
+
const jellyHost = fromEnv ? {
|
|
28722
|
+
actKey: readActKey(fromEnv.actKeyPath),
|
|
28723
|
+
label: fromEnv.label,
|
|
28724
|
+
kind: "jelly-host",
|
|
28725
|
+
realm: fromEnv.realm,
|
|
28726
|
+
...fromEnv.socketGroup !== void 0 ? { socketGroup: fromEnv.socketGroup } : {},
|
|
28727
|
+
...fromEnv.run !== void 0 ? { run: resolve(fromEnv.run) } : {}
|
|
28728
|
+
} : values["--deployment"] === "jelly" ? {
|
|
27917
28729
|
actKey: readActKey(resolve(values["--act-key"])),
|
|
27918
28730
|
label: jellyLabel(values["--label"]),
|
|
27919
28731
|
kind: "jelly-host",
|
|
@@ -27925,6 +28737,10 @@ async function daemonMain(argv) {
|
|
|
27925
28737
|
log,
|
|
27926
28738
|
...jellyHost ? { jellyHost } : {}
|
|
27927
28739
|
});
|
|
28740
|
+
if (jellyHost) log("info", "daemon.start", {
|
|
28741
|
+
deployment: "jelly",
|
|
28742
|
+
source
|
|
28743
|
+
});
|
|
27928
28744
|
} catch (error) {
|
|
27929
28745
|
process.stderr.write(`messagingd: ${error instanceof Error ? error.message : "failed to start"}\n`);
|
|
27930
28746
|
return 1;
|