shennian 0.3.86 → 0.4.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/README.md +3 -2
- package/dist/bin/shennian.js +1 -1
- package/dist/publish-build-manifest.json +32 -27
- package/dist/src/agents/adapter.js +1 -19
- package/dist/src/agents/claude-managed-permissions.js +1 -248
- package/dist/src/agents/claude.js +8 -393
- package/dist/src/agents/codex-managed-permissions.js +1 -294
- package/dist/src/agents/codex-managed-room-tools.js +1 -249
- package/dist/src/agents/codex-utils.js +7 -200
- package/dist/src/agents/codex.d.ts +7 -1
- package/dist/src/agents/codex.js +17 -1352
- package/dist/src/agents/config-status.js +1 -226
- package/dist/src/agents/cursor-managed-permissions.js +2 -299
- package/dist/src/agents/cursor.js +1 -314
- package/dist/src/agents/custom.js +4 -418
- package/dist/src/agents/external-channel-instructions.js +11 -94
- package/dist/src/agents/gemini.js +1 -180
- package/dist/src/agents/manager.js +18 -250
- package/dist/src/agents/model-registry/cache.js +1 -37
- package/dist/src/agents/model-registry/discovery.js +2 -188
- package/dist/src/agents/model-registry/parsers.js +4 -521
- package/dist/src/agents/model-registry/runner.js +1 -30
- package/dist/src/agents/model-registry/service.js +1 -115
- package/dist/src/agents/model-registry/types.js +1 -8
- package/dist/src/agents/model-registry.js +1 -19
- package/dist/src/agents/native-capabilities.js +1 -237
- package/dist/src/agents/openclaw.js +2 -293
- package/dist/src/agents/opencode-managed-permissions.js +2 -187
- package/dist/src/agents/opencode.js +1 -298
- package/dist/src/agents/platform-instructions.js +17 -46
- package/dist/src/agents/runtime-compatibility.js +1 -44
- package/dist/src/agents/workbuddy-managed-permissions.js +2 -329
- package/dist/src/agents/workbuddy.js +3 -444
- package/dist/src/commands/logout.d.ts +3 -0
- package/dist/src/commands/logout.js +1 -0
- package/dist/src/daemon-ipc/paths.js +1 -1
- package/dist/src/fs/boundary.js +1 -185
- package/dist/src/fs/handler.js +1 -130
- package/dist/src/fs/security.js +1 -32
- package/dist/src/fs/text-decoder.js +1 -110
- package/dist/src/index.js +3 -587
- package/dist/src/manager/prompt.js +29 -34
- package/dist/src/manager/registry.js +1 -317
- package/dist/src/manager/retired-external-channel-compat.js +1 -40
- package/dist/src/manager/runtime.js +23 -1514
- package/dist/src/native-fusion/codex-parser.d.ts +3 -1
- package/dist/src/native-fusion/codex-parser.js +4 -660
- package/dist/src/native-fusion/codex-user-input.js +5 -102
- package/dist/src/native-fusion/config.js +1 -5
- package/dist/src/native-fusion/file-watcher.js +1 -191
- package/dist/src/native-fusion/local-repository.js +2 -569
- package/dist/src/native-fusion/opencode-parser.js +3 -130
- package/dist/src/native-fusion/parser-common.d.ts +6 -1
- package/dist/src/native-fusion/parser-common.js +4 -4
- package/dist/src/native-fusion/parsers.d.ts +4 -2
- package/dist/src/native-fusion/parsers.js +3 -140
- package/dist/src/native-fusion/service.js +2 -739
- package/dist/src/native-fusion/session-header.js +2 -101
- package/dist/src/native-fusion/state.js +2 -104
- package/dist/src/native-fusion/types.js +1 -1
- package/dist/src/personal-sync/machine-authority.js +1 -34
- package/dist/src/room/activation-audit.js +2 -45
- package/dist/src/room/activation-policy.js +1 -30
- package/dist/src/room/activation-service.js +8 -408
- package/dist/src/room/invite-bootstrap.js +1 -156
- package/dist/src/room/managed-prompt-composer.d.ts +1 -1
- package/dist/src/room/managed-prompt-composer.js +17 -16
- package/dist/src/room/managed-room-binding.js +1 -167
- package/dist/src/room/managed-room-ipc.d.ts +4 -0
- package/dist/src/room/managed-room-ipc.js +2 -2
- package/dist/src/room/mirror-sync-service.d.ts +1 -0
- package/dist/src/room/mirror-sync-service.js +1 -183
- package/dist/src/room/room-binding-client.js +1 -151
- package/dist/src/session/archive-zip.js +1 -220
- package/dist/src/session/deletion-sync-state.js +2 -136
- package/dist/src/session/handlers/agent-capabilities.js +1 -14
- package/dist/src/session/handlers/agent-config.js +1 -150
- package/dist/src/session/handlers/agents.js +1 -55
- package/dist/src/session/handlers/chat.js +2 -1030
- package/dist/src/session/handlers/control.js +1 -61
- package/dist/src/session/handlers/fs.js +1 -594
- package/dist/src/session/handlers/message-status.js +1 -15
- package/dist/src/session/handlers/remote-access.js +1 -43
- package/dist/src/session/handlers/room-managed.js +1 -101
- package/dist/src/session/handlers/session-refresh.js +1 -35
- package/dist/src/session/handlers/title.js +1 -79
- package/dist/src/session/handlers/tool-detail.js +1 -149
- package/dist/src/session/handlers/voice-window.js +1 -62
- package/dist/src/session/history-subscriptions.js +1 -470
- package/dist/src/session/manager.d.ts +1 -0
- package/dist/src/session/manager.js +1 -658
- package/dist/src/session/native-cleanup-outbox.js +2 -287
- package/dist/src/session/native-session-lifecycle.js +6 -327
- package/dist/src/session/queue.d.ts +8 -0
- package/dist/src/session/queue.js +6 -6
- package/dist/src/upgrade/scheduler.js +1 -126
- package/node_modules/@shennian/wire/dist/agent-workspace.js +2 -2
- package/package.json +1 -1
|
@@ -1,101 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import { readTimestamp, safeParse } from './parser-common.js';
|
|
5
|
-
const MAX_HEADER_BYTES = 1024 * 1024;
|
|
6
|
-
const MAX_HEADER_RECORDS = 64;
|
|
7
|
-
export function inspectNativeSessionFile(filePath) {
|
|
8
|
-
if (filePath.endsWith('.opencode-session.json'))
|
|
9
|
-
return inspectOpenCodeSnapshot(filePath);
|
|
10
|
-
const records = readHeaderRecords(filePath);
|
|
11
|
-
if (records.length === 0)
|
|
12
|
-
return { status: 'pending' };
|
|
13
|
-
const stat = fs.statSync(filePath);
|
|
14
|
-
if (filePath.split(/[\\/]+/).includes('.codex')) {
|
|
15
|
-
const meta = records.find((record) => record.type === 'session_meta');
|
|
16
|
-
if (!meta)
|
|
17
|
-
return { status: 'pending' };
|
|
18
|
-
const payload = objectRecord(meta.payload);
|
|
19
|
-
const createdAtMs = readTimestamp(meta.timestamp)
|
|
20
|
-
?? readTimestamp(payload?.timestamp)
|
|
21
|
-
?? validBirthtime(stat.birthtimeMs);
|
|
22
|
-
const sourceSessionKey = typeof payload?.id === 'string' ? payload.id : '';
|
|
23
|
-
return createdAtMs && sourceSessionKey
|
|
24
|
-
? { status: 'ready', createdAtMs, sources: [{ agentType: 'codex', sourceSessionKey }] }
|
|
25
|
-
: { status: 'pending' };
|
|
26
|
-
}
|
|
27
|
-
const firstTimestamp = records.map((record) => readTimestamp(record.timestamp)).find((value) => value != null);
|
|
28
|
-
const createdAtMs = firstTimestamp ?? validBirthtime(stat.birthtimeMs);
|
|
29
|
-
const sourceSessionKey = pathBasenameWithoutJsonl(filePath);
|
|
30
|
-
return createdAtMs && sourceSessionKey
|
|
31
|
-
? { status: 'ready', createdAtMs, sources: [{ agentType: 'claude', sourceSessionKey }] }
|
|
32
|
-
: { status: 'pending' };
|
|
33
|
-
}
|
|
34
|
-
function inspectOpenCodeSnapshot(filePath) {
|
|
35
|
-
let parsed;
|
|
36
|
-
try {
|
|
37
|
-
parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return { status: 'pending' };
|
|
41
|
-
}
|
|
42
|
-
const root = objectRecord(parsed);
|
|
43
|
-
if (!root || !Array.isArray(root.sessions) || root.sessions.length === 0)
|
|
44
|
-
return { status: 'pending' };
|
|
45
|
-
const sourceCreatedAtByKey = {};
|
|
46
|
-
for (const value of root.sessions) {
|
|
47
|
-
const session = objectRecord(value);
|
|
48
|
-
const id = typeof session?.id === 'string' ? session.id : '';
|
|
49
|
-
const createdAtMs = normalizeNumericTimestamp(session?.time_created);
|
|
50
|
-
if (id && createdAtMs)
|
|
51
|
-
sourceCreatedAtByKey[id] = createdAtMs;
|
|
52
|
-
}
|
|
53
|
-
const timestamps = Object.values(sourceCreatedAtByKey);
|
|
54
|
-
if (timestamps.length === 0)
|
|
55
|
-
return { status: 'pending' };
|
|
56
|
-
return {
|
|
57
|
-
status: 'ready',
|
|
58
|
-
createdAtMs: Math.min(...timestamps),
|
|
59
|
-
sourceCreatedAtByKey,
|
|
60
|
-
sources: Object.keys(sourceCreatedAtByKey).map((sourceSessionKey) => ({
|
|
61
|
-
agentType: 'opencode',
|
|
62
|
-
sourceSessionKey,
|
|
63
|
-
})),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function pathBasenameWithoutJsonl(filePath) {
|
|
67
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
68
|
-
const basename = normalized.slice(normalized.lastIndexOf('/') + 1);
|
|
69
|
-
return basename.endsWith('.jsonl') ? basename.slice(0, -'.jsonl'.length) : basename;
|
|
70
|
-
}
|
|
71
|
-
function readHeaderRecords(filePath) {
|
|
72
|
-
const descriptor = fs.openSync(filePath, 'r');
|
|
73
|
-
try {
|
|
74
|
-
const size = Math.min(fs.fstatSync(descriptor).size, MAX_HEADER_BYTES);
|
|
75
|
-
if (size === 0)
|
|
76
|
-
return [];
|
|
77
|
-
const buffer = Buffer.allocUnsafe(size);
|
|
78
|
-
const bytesRead = fs.readSync(descriptor, buffer, 0, size, 0);
|
|
79
|
-
return buffer.subarray(0, bytesRead).toString('utf8').split('\n')
|
|
80
|
-
.slice(0, MAX_HEADER_RECORDS)
|
|
81
|
-
.map((line) => safeParse(line))
|
|
82
|
-
.filter((record) => record != null);
|
|
83
|
-
}
|
|
84
|
-
finally {
|
|
85
|
-
fs.closeSync(descriptor);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function objectRecord(value) {
|
|
89
|
-
return typeof value === 'object' && value !== null ? value : null;
|
|
90
|
-
}
|
|
91
|
-
function validBirthtime(value) {
|
|
92
|
-
return Number.isFinite(value) && value > 0 ? value : null;
|
|
93
|
-
}
|
|
94
|
-
export function normalizeNativeTimestamp(value) {
|
|
95
|
-
return normalizeNumericTimestamp(value);
|
|
96
|
-
}
|
|
97
|
-
function normalizeNumericTimestamp(value) {
|
|
98
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
99
|
-
return null;
|
|
100
|
-
return value < 10_000_000_000 ? Math.round(value * 1000) : Math.round(value);
|
|
101
|
-
}
|
|
1
|
+
import a from"node:fs";import{readTimestamp as d,safeParse as g}from"./parser-common.js";const S=1024*1024,b=64;function j(t){if(t.endsWith(".opencode-session.json"))return h(t);const s=A(t);if(s.length===0)return{status:"pending"};const e=a.statSync(t);if(t.split(/[\\/]+/).includes(".codex")){const n=s.find(y=>y.type==="session_meta");if(!n)return{status:"pending"};const c=p(n.payload),u=d(n.timestamp)??d(c?.timestamp)??f(e.birthtimeMs),m=typeof c?.id=="string"?c.id:"";return u&&m?{status:"ready",createdAtMs:u,sources:[{agentType:"codex",sourceSessionKey:m}]}:{status:"pending"}}const o=s.map(n=>d(n.timestamp)).find(n=>n!=null)??f(e.birthtimeMs),r=M(t);return o&&r?{status:"ready",createdAtMs:o,sources:[{agentType:"claude",sourceSessionKey:r}]}:{status:"pending"}}function h(t){let s;try{s=JSON.parse(a.readFileSync(t,"utf8"))}catch{return{status:"pending"}}const e=p(s);if(!e||!Array.isArray(e.sessions)||e.sessions.length===0)return{status:"pending"};const i={};for(const r of e.sessions){const n=p(r),c=typeof n?.id=="string"?n.id:"",u=l(n?.time_created);c&&u&&(i[c]=u)}const o=Object.values(i);return o.length===0?{status:"pending"}:{status:"ready",createdAtMs:Math.min(...o),sourceCreatedAtByKey:i,sources:Object.keys(i).map(r=>({agentType:"opencode",sourceSessionKey:r}))}}function M(t){const s=t.replace(/\\/g,"/"),e=s.slice(s.lastIndexOf("/")+1);return e.endsWith(".jsonl")?e.slice(0,-6):e}function A(t){const s=a.openSync(t,"r");try{const e=Math.min(a.fstatSync(s).size,S);if(e===0)return[];const i=Buffer.allocUnsafe(e),o=a.readSync(s,i,0,e,0);return i.subarray(0,o).toString("utf8").split(`
|
|
2
|
+
`).slice(0,b).map(r=>g(r)).filter(r=>r!=null)}finally{a.closeSync(s)}}function p(t){return typeof t=="object"&&t!==null?t:null}function f(t){return Number.isFinite(t)&&t>0?t:null}function R(t){return l(t)}function l(t){return typeof t!="number"||!Number.isFinite(t)||t<=0?null:t<1e10?Math.round(t*1e3):Math.round(t)}export{j as inspectNativeSessionFile,R as normalizeNativeTimestamp};
|
|
@@ -1,104 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import crypto from 'node:crypto';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { resolvePersonalSyncV1Path } from '../personal-sync/paths.js';
|
|
7
|
-
import { getDaemonInstallationId } from '../session/store.js';
|
|
8
|
-
import { readPersonalDataEpochMarker } from '../personal-sync/epoch.js';
|
|
9
|
-
import { NATIVE_SCANNER_STATE_SCHEMA_VERSION, } from './types.js';
|
|
10
|
-
function stateFile() {
|
|
11
|
-
return resolvePersonalSyncV1Path('native-fusion-state.json');
|
|
12
|
-
}
|
|
13
|
-
export function loadNativeScannerState() {
|
|
14
|
-
const file = stateFile();
|
|
15
|
-
const daemonInstallationId = getDaemonInstallationId();
|
|
16
|
-
if (!fs.existsSync(file)) {
|
|
17
|
-
const epochStartedAt = readPersonalDataEpochMarker()?.epochStartedAt;
|
|
18
|
-
const created = {
|
|
19
|
-
schemaVersion: NATIVE_SCANNER_STATE_SCHEMA_VERSION,
|
|
20
|
-
fusionEpochMs: epochStartedAt ? Date.parse(epochStartedAt) : Date.now(),
|
|
21
|
-
daemonInstallationId,
|
|
22
|
-
files: {},
|
|
23
|
-
};
|
|
24
|
-
atomicWriteJson(file, created);
|
|
25
|
-
return created;
|
|
26
|
-
}
|
|
27
|
-
let parsed;
|
|
28
|
-
try {
|
|
29
|
-
parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
30
|
-
}
|
|
31
|
-
catch (cause) {
|
|
32
|
-
throw new Error(`native fusion v1 state is unreadable: ${file}`, { cause });
|
|
33
|
-
}
|
|
34
|
-
validateState(parsed, daemonInstallationId);
|
|
35
|
-
return parsed;
|
|
36
|
-
}
|
|
37
|
-
export function saveNativeScannerState(state) {
|
|
38
|
-
const daemonInstallationId = getDaemonInstallationId();
|
|
39
|
-
validateState(state, daemonInstallationId);
|
|
40
|
-
const file = stateFile();
|
|
41
|
-
if (fs.existsSync(file)) {
|
|
42
|
-
const persisted = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
43
|
-
validateState(persisted, daemonInstallationId);
|
|
44
|
-
if (persisted.fusionEpochMs !== state.fusionEpochMs) {
|
|
45
|
-
throw new Error('native fusion epoch is immutable');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
atomicWriteJson(file, state);
|
|
49
|
-
}
|
|
50
|
-
function validateState(state, daemonInstallationId) {
|
|
51
|
-
if (!state
|
|
52
|
-
|| state.schemaVersion !== NATIVE_SCANNER_STATE_SCHEMA_VERSION
|
|
53
|
-
|| !Number.isSafeInteger(state.fusionEpochMs)
|
|
54
|
-
|| state.fusionEpochMs <= 0
|
|
55
|
-
|| state.daemonInstallationId !== daemonInstallationId
|
|
56
|
-
|| !state.files
|
|
57
|
-
|| typeof state.files !== 'object'
|
|
58
|
-
|| Array.isArray(state.files)) {
|
|
59
|
-
throw new Error('invalid native fusion v1 state');
|
|
60
|
-
}
|
|
61
|
-
for (const cursor of Object.values(state.files)) {
|
|
62
|
-
if (cursor.daemonInstallationId !== daemonInstallationId) {
|
|
63
|
-
throw new Error('native fusion cursor belongs to another daemon installation');
|
|
64
|
-
}
|
|
65
|
-
if (cursor.sources !== undefined && (!Array.isArray(cursor.sources)
|
|
66
|
-
|| cursor.sources.some((source) => (!source
|
|
67
|
-
|| !['codex', 'claude', 'opencode'].includes(source.agentType)
|
|
68
|
-
|| typeof source.sourceSessionKey !== 'string'
|
|
69
|
-
|| !source.sourceSessionKey)))) {
|
|
70
|
-
throw new Error('native fusion cursor has invalid source metadata');
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function atomicWriteJson(file, value) {
|
|
75
|
-
const directory = path.dirname(file);
|
|
76
|
-
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
77
|
-
const temp = `${file}.tmp-${process.pid}-${crypto.randomBytes(6).toString('hex')}`;
|
|
78
|
-
let descriptor;
|
|
79
|
-
try {
|
|
80
|
-
descriptor = fs.openSync(temp, 'wx', 0o600);
|
|
81
|
-
fs.writeFileSync(descriptor, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
82
|
-
fs.fsyncSync(descriptor);
|
|
83
|
-
fs.closeSync(descriptor);
|
|
84
|
-
descriptor = undefined;
|
|
85
|
-
fs.renameSync(temp, file);
|
|
86
|
-
if (process.platform !== 'win32') {
|
|
87
|
-
const directoryDescriptor = fs.openSync(directory, 'r');
|
|
88
|
-
try {
|
|
89
|
-
fs.fsyncSync(directoryDescriptor);
|
|
90
|
-
}
|
|
91
|
-
finally {
|
|
92
|
-
fs.closeSync(directoryDescriptor);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
finally {
|
|
97
|
-
if (descriptor !== undefined)
|
|
98
|
-
fs.closeSync(descriptor);
|
|
99
|
-
try {
|
|
100
|
-
fs.unlinkSync(temp);
|
|
101
|
-
}
|
|
102
|
-
catch { }
|
|
103
|
-
}
|
|
104
|
-
}
|
|
1
|
+
import d from"node:crypto";import r from"node:fs";import p from"node:path";import{resolvePersonalSyncV1Path as y}from"../personal-sync/paths.js";import{getDaemonInstallationId as a}from"../session/store.js";import{readPersonalDataEpochMarker as m}from"../personal-sync/epoch.js";import{NATIVE_SCANNER_STATE_SCHEMA_VERSION as f}from"./types.js";function l(){return y("native-fusion-state.json")}function N(){const e=l(),t=a();if(!r.existsSync(e)){const n=m()?.epochStartedAt,i={schemaVersion:f,fusionEpochMs:n?Date.parse(n):Date.now(),daemonInstallationId:t,files:{}};return u(e,i),i}let o;try{o=JSON.parse(r.readFileSync(e,"utf8"))}catch(n){throw new Error(`native fusion v1 state is unreadable: ${e}`,{cause:n})}return s(o,t),o}function g(e){const t=a();s(e,t);const o=l();if(r.existsSync(o)){const n=JSON.parse(r.readFileSync(o,"utf8"));if(s(n,t),n.fusionEpochMs!==e.fusionEpochMs)throw new Error("native fusion epoch is immutable")}u(o,e)}function s(e,t){if(!e||e.schemaVersion!==f||!Number.isSafeInteger(e.fusionEpochMs)||e.fusionEpochMs<=0||e.daemonInstallationId!==t||!e.files||typeof e.files!="object"||Array.isArray(e.files))throw new Error("invalid native fusion v1 state");for(const o of Object.values(e.files)){if(o.daemonInstallationId!==t)throw new Error("native fusion cursor belongs to another daemon installation");if(o.sources!==void 0&&(!Array.isArray(o.sources)||o.sources.some(n=>!n||!["codex","claude","opencode"].includes(n.agentType)||typeof n.sourceSessionKey!="string"||!n.sourceSessionKey)))throw new Error("native fusion cursor has invalid source metadata")}}function u(e,t){const o=p.dirname(e);r.mkdirSync(o,{recursive:!0,mode:448});const n=`${e}.tmp-${process.pid}-${d.randomBytes(6).toString("hex")}`;let i;try{if(i=r.openSync(n,"wx",384),r.writeFileSync(i,`${JSON.stringify(t,null,2)}
|
|
2
|
+
`,"utf8"),r.fsyncSync(i),r.closeSync(i),i=void 0,r.renameSync(n,e),process.platform!=="win32"){const c=r.openSync(o,"r");try{r.fsyncSync(c)}finally{r.closeSync(c)}}}finally{i!==void 0&&r.closeSync(i);try{r.unlinkSync(n)}catch{}}}export{N as loadNativeScannerState,g as saveNativeScannerState};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
const E=1;export{E as NATIVE_SCANNER_STATE_SCHEMA_VERSION};
|
|
@@ -1,34 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// @test src/__tests__/personal-sync-account-namespace.test.ts
|
|
3
|
-
import { SESSION_SYNC_DATA_EPOCH } from '@shennian/wire';
|
|
4
|
-
import { loadConfig, saveConfig } from '../config/index.js';
|
|
5
|
-
import { getDaemonInstallationId } from './identity.js';
|
|
6
|
-
export async function synchronizeMachinePersonalAuthority(input) {
|
|
7
|
-
const response = await (input.fetchImpl ?? fetch)(`${input.serverUrl.replace(/\/+$/, '')}/api/machines/identity`, {
|
|
8
|
-
headers: { Authorization: `Bearer ${input.machineToken}` },
|
|
9
|
-
signal: AbortSignal.timeout(10_000),
|
|
10
|
-
});
|
|
11
|
-
if (!response.ok)
|
|
12
|
-
throw new Error(`machine_personal_authority_unavailable:${response.status}`);
|
|
13
|
-
const body = await response.json();
|
|
14
|
-
if (typeof body.machineId !== 'string' || !body.machineId
|
|
15
|
-
|| typeof body.daemonInstallationId !== 'string' || !body.daemonInstallationId
|
|
16
|
-
|| typeof body.dataEpoch !== 'number' || !Number.isSafeInteger(body.dataEpoch))
|
|
17
|
-
throw new Error('machine_personal_authority_invalid_response');
|
|
18
|
-
if (input.machineId && body.machineId !== input.machineId) {
|
|
19
|
-
throw new Error('machine_personal_authority_machine_mismatch');
|
|
20
|
-
}
|
|
21
|
-
if (body.daemonInstallationId !== getDaemonInstallationId()) {
|
|
22
|
-
throw new Error('machine_personal_authority_daemon_mismatch');
|
|
23
|
-
}
|
|
24
|
-
if (body.dataEpoch !== SESSION_SYNC_DATA_EPOCH) {
|
|
25
|
-
throw new Error('machine_personal_authority_epoch_mismatch');
|
|
26
|
-
}
|
|
27
|
-
const config = loadConfig();
|
|
28
|
-
if (config.machineId !== body.machineId) {
|
|
29
|
-
// accountNamespaceId is deliberately left untouched as legacy pairing
|
|
30
|
-
// metadata. It never selects, clears, or authorizes personal storage.
|
|
31
|
-
saveConfig({ ...config, machineId: body.machineId });
|
|
32
|
-
}
|
|
33
|
-
return body;
|
|
34
|
-
}
|
|
1
|
+
import{SESSION_SYNC_DATA_EPOCH as r}from"@shennian/wire";import{loadConfig as i,saveConfig as t}from"../config/index.js";import{getDaemonInstallationId as h}from"./identity.js";async function d(e){const n=await(e.fetchImpl??fetch)(`${e.serverUrl.replace(/\/+$/,"")}/api/machines/identity`,{headers:{Authorization:`Bearer ${e.machineToken}`},signal:AbortSignal.timeout(1e4)});if(!n.ok)throw new Error(`machine_personal_authority_unavailable:${n.status}`);const a=await n.json();if(typeof a.machineId!="string"||!a.machineId||typeof a.daemonInstallationId!="string"||!a.daemonInstallationId||typeof a.dataEpoch!="number"||!Number.isSafeInteger(a.dataEpoch))throw new Error("machine_personal_authority_invalid_response");if(e.machineId&&a.machineId!==e.machineId)throw new Error("machine_personal_authority_machine_mismatch");if(a.daemonInstallationId!==h())throw new Error("machine_personal_authority_daemon_mismatch");if(a.dataEpoch!==r)throw new Error("machine_personal_authority_epoch_mismatch");const o=i();return o.machineId!==a.machineId&&t({...o,machineId:a.machineId}),a}export{d as synchronizeMachinePersonalAuthority};
|
|
@@ -1,45 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { resolveShennianPath } from '../config/index.js';
|
|
6
|
-
export class RoomActivationAudit {
|
|
7
|
-
root;
|
|
8
|
-
constructor(root = resolveShennianPath('rooms')) {
|
|
9
|
-
this.root = root;
|
|
10
|
-
}
|
|
11
|
-
record(event) {
|
|
12
|
-
for (const value of [event.roomId, event.bindingId, event.causeMessageId, event.rootCauseMessageId]) {
|
|
13
|
-
if (!/^[A-Za-z0-9_.:@-]{1,191}$/.test(value))
|
|
14
|
-
throw new Error('invalid_room_activation_audit_id');
|
|
15
|
-
}
|
|
16
|
-
if (!Number.isInteger(event.hopCount) || event.hopCount < 0 || event.hopCount > 32) {
|
|
17
|
-
throw new Error('invalid_room_activation_audit_hop');
|
|
18
|
-
}
|
|
19
|
-
const directory = path.join(this.root, event.roomId);
|
|
20
|
-
fs.mkdirSync(this.root, { recursive: true, mode: 0o700 });
|
|
21
|
-
if (fs.existsSync(directory)) {
|
|
22
|
-
const stat = fs.lstatSync(directory);
|
|
23
|
-
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
24
|
-
throw new Error('unsafe_room_activation_audit_path');
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
fs.mkdirSync(directory, { mode: 0o700 });
|
|
28
|
-
}
|
|
29
|
-
if (process.platform !== 'win32')
|
|
30
|
-
fs.chmodSync(directory, 0o700);
|
|
31
|
-
const file = path.join(directory, 'activation-audit.jsonl');
|
|
32
|
-
if (fs.existsSync(file)) {
|
|
33
|
-
const stat = fs.lstatSync(file);
|
|
34
|
-
if (!stat.isFile() || stat.isSymbolicLink())
|
|
35
|
-
throw new Error('unsafe_room_activation_audit_path');
|
|
36
|
-
}
|
|
37
|
-
fs.appendFileSync(file, `${JSON.stringify({
|
|
38
|
-
schemaVersion: 1,
|
|
39
|
-
...event,
|
|
40
|
-
at: (event.at ?? new Date()).toISOString(),
|
|
41
|
-
})}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
42
|
-
if (process.platform !== 'win32')
|
|
43
|
-
fs.chmodSync(file, 0o600);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
1
|
+
import i from"node:fs";import n from"node:path";import{resolveShennianPath as a}from"../config/index.js";class f{root;constructor(o=a("rooms")){this.root=o}record(o){for(const t of[o.roomId,o.bindingId,o.causeMessageId,o.rootCauseMessageId])if(!/^[A-Za-z0-9_.:@-]{1,191}$/.test(t))throw new Error("invalid_room_activation_audit_id");if(!Number.isInteger(o.hopCount)||o.hopCount<0||o.hopCount>32)throw new Error("invalid_room_activation_audit_hop");const r=n.join(this.root,o.roomId);if(i.mkdirSync(this.root,{recursive:!0,mode:448}),i.existsSync(r)){const t=i.lstatSync(r);if(!t.isDirectory()||t.isSymbolicLink())throw new Error("unsafe_room_activation_audit_path")}else i.mkdirSync(r,{mode:448});process.platform!=="win32"&&i.chmodSync(r,448);const s=n.join(r,"activation-audit.jsonl");if(i.existsSync(s)){const t=i.lstatSync(s);if(!t.isFile()||t.isSymbolicLink())throw new Error("unsafe_room_activation_audit_path")}i.appendFileSync(s,`${JSON.stringify({schemaVersion:1,...o,at:(o.at??new Date).toISOString()})}
|
|
2
|
+
`,{encoding:"utf8",mode:384}),process.platform!=="win32"&&i.chmodSync(s,384)}}export{f as RoomActivationAudit};
|
|
@@ -1,30 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// @test src/__tests__/room-activation-policy.test.ts
|
|
3
|
-
export function selectRoomActivationTargets(message, registrations) {
|
|
4
|
-
if (message.deletedAt || message.kind === 'system')
|
|
5
|
-
return [];
|
|
6
|
-
const mentionedAgents = new Set((message.mentions ?? [])
|
|
7
|
-
.filter((mention) => mention.principalType === 'agent')
|
|
8
|
-
.map((mention) => mention.membershipId));
|
|
9
|
-
const eligible = registrations.filter(isEligible);
|
|
10
|
-
if (message.author.principalType === 'agent') {
|
|
11
|
-
return eligible
|
|
12
|
-
.filter((registration) => registration.binding.agentMembershipId !== message.author.membershipId &&
|
|
13
|
-
mentionedAgents.has(registration.binding.agentMembershipId))
|
|
14
|
-
.map((registration) => ({ registration, cause: 'explicit_mention' }));
|
|
15
|
-
}
|
|
16
|
-
if (mentionedAgents.size > 0) {
|
|
17
|
-
return eligible
|
|
18
|
-
.filter((registration) => mentionedAgents.has(registration.binding.agentMembershipId))
|
|
19
|
-
.map((registration) => ({ registration, cause: 'explicit_mention' }));
|
|
20
|
-
}
|
|
21
|
-
return eligible
|
|
22
|
-
.filter((registration) => registration.binding.activationMode === 'auto_follow')
|
|
23
|
-
.map((registration) => ({ registration, cause: 'human_message' }));
|
|
24
|
-
}
|
|
25
|
-
function isEligible(registration) {
|
|
26
|
-
return Boolean(registration.lifecycleState !== 'closed' &&
|
|
27
|
-
registration.binding?.status === 'active' &&
|
|
28
|
-
registration.binding.activationMode !== 'paused' &&
|
|
29
|
-
registration.binding.agentMembershipId);
|
|
30
|
-
}
|
|
1
|
+
function l(i,a){if(i.deletedAt||i.kind==="system")return[];const n=new Set((i.mentions??[]).filter(e=>e.principalType==="agent").map(e=>e.membershipId)),t=a.filter(d);return i.author.principalType==="agent"?t.filter(e=>e.binding.agentMembershipId!==i.author.membershipId&&n.has(e.binding.agentMembershipId)).map(e=>({registration:e,cause:"explicit_mention"})):n.size>0?t.filter(e=>n.has(e.binding.agentMembershipId)).map(e=>({registration:e,cause:"explicit_mention"})):t.filter(e=>e.binding.activationMode==="auto_follow").map(e=>({registration:e,cause:"human_message"}))}function d(i){return!!(i.lifecycleState!=="closed"&&i.binding?.status==="active"&&i.binding.activationMode!=="paused"&&i.binding.agentMembershipId)}export{l as selectRoomActivationTargets};
|