fedipod 1.49.2 → 1.50.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/fedipod.mjs +2 -5
- package/cli.md +5 -10
- package/lib/client/masto/index.mjs +0 -3
- package/lib/client/masto/oauth.mjs +23 -112
- package/lib/client/masto/render.mjs +11 -4
- package/lib/core/agent.mjs +3 -12
- package/lib/core/graphview.mjs +9 -1
- package/lib/core/publisher/restore.mjs +1 -0
- package/lib/core/store.mjs +27 -6
- package/lib/device/cli/commands/setup.mjs +1 -24
- package/lib/device/setup.mjs +1 -5
- package/lib/shared/guard.mjs +2 -2
- package/lib/surface/routes/owner.mjs +2 -12
- package/lib/surface/surface.mjs +1 -2
- package/package.json +2 -2
- package/web/admin/index.html +1 -1
- package/web/admin/record.js +0 -3
- package/web/app/admin-facade.mjs +0 -6
- package/web/app/agent.mjs +3 -1
- package/web/app/dist/sw.js +220 -151
- package/web/app/dist/sw.js.map +3 -3
|
@@ -5,7 +5,6 @@ import fs from 'node:fs';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { announceModeration } from '../../core/social.mjs';
|
|
7
7
|
import { addRemoveActivity, publicHandle } from '../../core/wire.mjs';
|
|
8
|
-
import { hashPassword } from '../../client/masto/index.mjs';
|
|
9
8
|
import { tildify } from '../../shared/files.mjs';
|
|
10
9
|
import { AGENT_VERSION } from '../static.mjs';
|
|
11
10
|
|
|
@@ -71,8 +70,6 @@ export async function get(p, ctx, req, res, url) { // eslint-disable-line no-u
|
|
|
71
70
|
approveJoins: !!cfg.approveJoins, review: !!cfg.review,
|
|
72
71
|
aliases: cfg.aliases || [],
|
|
73
72
|
autoAcceptFollows: cfg.autoAcceptFollows !== false,
|
|
74
|
-
// never the record itself
|
|
75
|
-
hasUiPassword: !!cfg.uiPassword,
|
|
76
73
|
quiescedAt: cfg.quiescedAt || null, movedTo: cfg.movedTo || null,
|
|
77
74
|
// Per-machine, so it comes from the credential file, not pod config —
|
|
78
75
|
// and it is a fact here, not a setting: moving it means moving data,
|
|
@@ -172,8 +169,8 @@ export async function post(p, body, ctx, req, res) { // eslint-disable-line no
|
|
|
172
169
|
// A user acting here outranks an idle active agent on another device:
|
|
173
170
|
// claim the lease rather than write state that one will clobber.
|
|
174
171
|
await agent.requestTakeover?.();
|
|
175
|
-
// Merge, never replace:
|
|
176
|
-
//
|
|
172
|
+
// Merge, never replace: anything set later must survive an edit that
|
|
173
|
+
// never mentions it.
|
|
177
174
|
const cfg = { ...agent.store.getConfig() };
|
|
178
175
|
if ('name' in body) {
|
|
179
176
|
if (!body.name) return json(res, 400, { error: 'a display name is required' });
|
|
@@ -210,12 +207,6 @@ export async function post(p, body, ctx, req, res) { // eslint-disable-line no
|
|
|
210
207
|
};
|
|
211
208
|
cfg.moderators = next;
|
|
212
209
|
}
|
|
213
|
-
if ('password' in body) {
|
|
214
|
-
// '' clears it. The UI password is what turns the instant OAuth
|
|
215
|
-
// redirect into a login form; switching that off is a real choice.
|
|
216
|
-
if (body.password) cfg.uiPassword = hashPassword(body.password);
|
|
217
|
-
else delete cfg.uiPassword;
|
|
218
|
-
}
|
|
219
210
|
const republish = WIRE_CONFIG.some(k => k in body);
|
|
220
211
|
agent.store.setConfig(cfg);
|
|
221
212
|
if ('autoAcceptFollows' in body && agent.publisher) {
|
|
@@ -257,7 +248,6 @@ export async function post(p, body, ctx, req, res) { // eslint-disable-line no
|
|
|
257
248
|
approveJoins: !!cfg.approveJoins, review: !!cfg.review,
|
|
258
249
|
autoAcceptFollows: cfg.autoAcceptFollows !== false,
|
|
259
250
|
...(isGroup() ? { moderators: cfg.moderators || [] } : {}),
|
|
260
|
-
hasUiPassword: !!cfg.uiPassword,
|
|
261
251
|
},
|
|
262
252
|
});
|
|
263
253
|
}
|
package/lib/surface/surface.mjs
CHANGED
|
@@ -239,8 +239,7 @@ export function buildAdminSurface({ agent, gate, allowed, log = console.log,
|
|
|
239
239
|
// twice over: a group has statuses (what it carried) and notifications
|
|
240
240
|
// (who joined), and its operator has a bio to edit and a profile they
|
|
241
241
|
// want to see the way everyone else does. The surface this opens is a
|
|
242
|
-
// login and client tokens,
|
|
243
|
-
// so a group can be gated exactly like a person before it is exposed.
|
|
242
|
+
// login and client tokens, approved at the pod like a person's.
|
|
244
243
|
if (p.startsWith('/api/') || p.startsWith('/oauth/')) {
|
|
245
244
|
if (await masto.handle(req, res, p, url)) return;
|
|
246
245
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fedipod",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.50.2",
|
|
4
4
|
"description": "Standalone single-actor ActivityPub agent whose wire face, RDF truth and state all live on a Solid pod (CSS). Bundles a Phanpy UI and a Mastodon client-API facade.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"install-service": "node bin/fedipod.mjs install-service",
|
|
56
56
|
"uninstall-service": "node bin/fedipod.mjs uninstall-service",
|
|
57
57
|
"prepublishOnly": "npm test",
|
|
58
|
-
"test": "node claude/smoke-tests/agent-smoke.mjs && node claude/smoke-tests/front-hard-test.mjs && node claude/smoke-tests/copy-test.mjs && node claude/smoke-tests/copy-mode-test.mjs && node claude/smoke-tests/hold-test.mjs && node claude/smoke-tests/masto-gateway-test.mjs && node scripts/check-pod-calls.mjs && node scripts/verify-ui.mjs && node --test claude/smoke-tests/forum-signup-form.test.mjs claude/smoke-tests/door-verify.test.mjs claude/smoke-tests/oauth-signin.test.mjs claude/smoke-tests/app-registration.test.mjs claude/smoke-tests/push-signout.test.mjs claude/smoke-tests/token-fetches.test.mjs claude/smoke-tests/signin-hardening.test.mjs claude/smoke-tests/load-fixes.test.mjs && node scripts/check-sizes.mjs && node scripts/test-dependents.mjs && node claude/validation/browser-agent/all.mjs",
|
|
58
|
+
"test": "node claude/smoke-tests/agent-smoke.mjs && node claude/smoke-tests/front-hard-test.mjs && node claude/smoke-tests/copy-test.mjs && node claude/smoke-tests/copy-mode-test.mjs && node claude/smoke-tests/hold-test.mjs && node claude/smoke-tests/masto-gateway-test.mjs && node scripts/check-pod-calls.mjs && node scripts/verify-ui.mjs && node --test claude/smoke-tests/forum-signup-form.test.mjs claude/smoke-tests/door-verify.test.mjs claude/smoke-tests/oauth-signin.test.mjs claude/smoke-tests/app-registration.test.mjs claude/smoke-tests/push-signout.test.mjs claude/smoke-tests/token-fetches.test.mjs claude/smoke-tests/signin-hardening.test.mjs claude/smoke-tests/load-fixes.test.mjs claude/smoke-tests/unread-docs.test.mjs claude/smoke-tests/post-text-once.test.mjs && node scripts/check-sizes.mjs && node scripts/test-dependents.mjs && node claude/validation/browser-agent/all.mjs",
|
|
59
59
|
"verify-ui": "node scripts/verify-ui.mjs",
|
|
60
60
|
"build:sw": "node scripts/build-app.mjs --entry web/app/sw-src.mjs --out web/app/dist/sw.js",
|
|
61
61
|
"build:boot": "node scripts/build-app.mjs --entry web/app/boot.mjs --out web/app/dist/boot.js",
|
package/web/admin/index.html
CHANGED
|
@@ -220,7 +220,7 @@ pre { overflow-x: auto; background: #0001; padding: .6rem; border-radius: .3rem;
|
|
|
220
220
|
no page there to open. Plain links — a top-level navigation sends no
|
|
221
221
|
Origin, so each agent's own firewall lets it through. -->
|
|
222
222
|
<section id="pane-others" aria-label="Actors on this machine" hidden>
|
|
223
|
-
<!-- Everything setup cannot work without. The bio
|
|
223
|
+
<!-- Everything setup cannot work without. The bio and avatar it
|
|
224
224
|
also offers are all changeable afterwards, so they are not asked here —
|
|
225
225
|
the fuller form is what the new actor opens on. -->
|
|
226
226
|
<form id="new-actor-form" hidden>
|
package/web/admin/record.js
CHANGED
|
@@ -7,9 +7,6 @@
|
|
|
7
7
|
// Two rules the routes enforce and this page respects: a write is a MERGE, so a
|
|
8
8
|
// form that never mentions a field cannot delete it; and anything on the wire is
|
|
9
9
|
// not real until the actor is republished, which POST /config does for itself.
|
|
10
|
-
//
|
|
11
|
-
// The UI password is deliberately not here — `fedipod passwd` sets it. It
|
|
12
|
-
// only gates /oauth/authorize, so it does nothing for a loopback-only agent.
|
|
13
10
|
|
|
14
11
|
let config = null;
|
|
15
12
|
// Held, because render() moves it into a generated row and empties that row's
|
package/web/app/admin-facade.mjs
CHANGED
|
@@ -18,7 +18,6 @@ import { publicHandle, webfingerHost } from '../../lib/core/wire.mjs';
|
|
|
18
18
|
import * as podInbox from '../../lib/pod/inbox.mjs';
|
|
19
19
|
import { normalizeImport, IMPORT_KINDS } from '../../lib/connections/import.mjs';
|
|
20
20
|
import { followActor, followHandle } from '../../lib/core/social.mjs';
|
|
21
|
-
import { hashPassword } from '../../lib/client/masto/index.mjs';
|
|
22
21
|
|
|
23
22
|
// The identity itself — changing any means a different actor, i.e. a new setup.
|
|
24
23
|
const PERMANENT_CONFIG = ['handle', 'remotePod', 'issuer', 'root', 'kind'];
|
|
@@ -121,7 +120,6 @@ export class AdminFacade {
|
|
|
121
120
|
image: cfg.image || null, fields: cfg.fields || [],
|
|
122
121
|
aliases: cfg.aliases || [],
|
|
123
122
|
autoAcceptFollows: cfg.autoAcceptFollows !== false,
|
|
124
|
-
hasUiPassword: !!cfg.uiPassword,
|
|
125
123
|
quiescedAt: cfg.quiescedAt || null, movedTo: cfg.movedTo || null,
|
|
126
124
|
mode: a.status().mode,
|
|
127
125
|
// Live from the credential's actual backend on THIS browser (a
|
|
@@ -218,10 +216,6 @@ export class AdminFacade {
|
|
|
218
216
|
.map(f => ({ name: String(f.name).trim(), value: String(f.value ?? '').trim() }));
|
|
219
217
|
}
|
|
220
218
|
if ('autoAcceptFollows' in body) cfg.autoAcceptFollows = !!body.autoAcceptFollows;
|
|
221
|
-
if ('password' in body) {
|
|
222
|
-
if (body.password) cfg.uiPassword = hashPassword(body.password);
|
|
223
|
-
else delete cfg.uiPassword;
|
|
224
|
-
}
|
|
225
219
|
const republish = WIRE_CONFIG.some(k => k in body);
|
|
226
220
|
a.store.setConfig(cfg);
|
|
227
221
|
if ('autoAcceptFollows' in body && a.publisher) a.publisher.config.autoAcceptFollows = cfg.autoAcceptFollows;
|
package/web/app/agent.mjs
CHANGED
|
@@ -516,7 +516,9 @@ export class BrowserAgent {
|
|
|
516
516
|
let unread = null;
|
|
517
517
|
const fullLoad = () => this.store.load().catch((e) => { unread = e; });
|
|
518
518
|
// Warm: only documents caught mid-write are read again, else a full load.
|
|
519
|
-
|
|
519
|
+
// A kept copy without the container's ETag is one whose last load could
|
|
520
|
+
// not read everything: what it lacks is still on the pod, unread.
|
|
521
|
+
if (this._warm && warmFrom.listingEtag) await this.store.refresh(this._warm.pending).catch(fullLoad);
|
|
520
522
|
else await fullLoad();
|
|
521
523
|
// Config: handed in on sign-up, or read from the pod on a returning sign-in.
|
|
522
524
|
const cfg = config || this.store.getConfig();
|