Store the person uuid the master issues, so renames keep our identity
The welcome now carries a personId; it lands in the instance_id setting and rides on every hello and HTTP report from then on. The master keys the person by that uuid, so changing instance_name moves our history to the new name instead of starting a stranger. Read-only in Settings on purpose — editing it by hand would detach this machine from its person. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
de9cbffd83
commit
a088381749
@@ -9,6 +9,9 @@ export async function GET() {
|
|||||||
for (const key of VALID_KEYS) {
|
for (const key of VALID_KEYS) {
|
||||||
result[key] = getSetting(key);
|
result[key] = getSetting(key);
|
||||||
}
|
}
|
||||||
|
// Read-only on purpose (not in VALID_KEYS): the master issues this uuid,
|
||||||
|
// and editing it by hand would detach the instance from its person.
|
||||||
|
result.instance_id = getSetting('instance_id');
|
||||||
return NextResponse.json(result);
|
return NextResponse.json(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -17,6 +17,7 @@ interface AppSettings {
|
|||||||
max_concurrent_accounts: string | null;
|
max_concurrent_accounts: string | null;
|
||||||
master_dashboard_url: string | null;
|
master_dashboard_url: string | null;
|
||||||
instance_name: string | null;
|
instance_name: string | null;
|
||||||
|
instance_id: string | null;
|
||||||
trading_hours: string | null;
|
trading_hours: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ export default function SettingsPage() {
|
|||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [syncing, setSyncing] = useState(false);
|
const [syncing, setSyncing] = useState(false);
|
||||||
const [instanceName, setInstanceName] = useState<string>('');
|
const [instanceName, setInstanceName] = useState<string>('');
|
||||||
|
const [instanceId, setInstanceId] = useState<string>('');
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [saved, setSaved] = useState(false);
|
const [saved, setSaved] = useState(false);
|
||||||
|
|
||||||
@@ -75,6 +77,9 @@ export default function SettingsPage() {
|
|||||||
if (s.instance_name != null) {
|
if (s.instance_name != null) {
|
||||||
setInstanceName(s.instance_name);
|
setInstanceName(s.instance_name);
|
||||||
}
|
}
|
||||||
|
if (s.instance_id != null) {
|
||||||
|
setInstanceId(s.instance_id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load cached contracts; if cache is empty, auto-resolve
|
// Load cached contracts; if cache is empty, auto-resolve
|
||||||
@@ -211,7 +216,11 @@ export default function SettingsPage() {
|
|||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-slate-800">Instance Name</p>
|
<p className="text-sm font-medium text-slate-800">Instance Name</p>
|
||||||
<p className="text-xs text-slate-400 mt-0.5">
|
<p className="text-xs text-slate-400 mt-0.5">
|
||||||
Identifier for this autotrader on the master dashboard
|
Identifier for this autotrader on the master dashboard.
|
||||||
|
Renaming is safe — the master tracks this machine by its
|
||||||
|
person ID{instanceId ? (
|
||||||
|
<> (<span className="font-mono">{instanceId.slice(0, 8)}</span>)</>
|
||||||
|
) : ' once it has connected'}, so history follows the new name.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ seedSetting.run('max_concurrent_accounts', '5');
|
|||||||
seedSetting.run('tick_interval_seconds', '60');
|
seedSetting.run('tick_interval_seconds', '60');
|
||||||
seedSetting.run('master_dashboard_url', 'https://master.juicerroom.com');
|
seedSetting.run('master_dashboard_url', 'https://master.juicerroom.com');
|
||||||
seedSetting.run('instance_name', '');
|
seedSetting.run('instance_name', '');
|
||||||
|
// The person uuid the master issues in its welcome. Stored, then presented on
|
||||||
|
// every hello/report — it is what lets a renamed instance_name stay the same
|
||||||
|
// person on the master. Never edited by hand.
|
||||||
|
seedSetting.run('instance_id', '');
|
||||||
seedSetting.run('trading_hours', 'full_cme');
|
seedSetting.run('trading_hours', 'full_cme');
|
||||||
// Solves the captchas the AutoBuyer runs into. Blank uses the key hardcoded in
|
// Solves the captchas the AutoBuyer runs into. Blank uses the key hardcoded in
|
||||||
// app/api/autobuyer/captcha; set this (or TWOCAPTCHA_API_KEY) to bill another
|
// app/api/autobuyer/captcha; set this (or TWOCAPTCHA_API_KEY) to bill another
|
||||||
|
|||||||
+9
-1
@@ -15,7 +15,7 @@
|
|||||||
* master you control, and prefer https so it cannot be redirected in transit.
|
* master you control, and prefer https so it cannot be redirected in transit.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getSetting, getInstruments, getRunnerHeartbeat, RUNNER_TIMEOUT_MS } from './db';
|
import { getSetting, setSetting, getInstruments, getRunnerHeartbeat, RUNNER_TIMEOUT_MS } from './db';
|
||||||
import { runTrade, startScheduler, stopScheduler, getSchedulerStatus } from './auto-trade';
|
import { runTrade, startScheduler, stopScheduler, getSchedulerStatus } from './auto-trade';
|
||||||
import { FIRMS, automationKey } from './automations';
|
import { FIRMS, automationKey } from './automations';
|
||||||
import { POINT_VALUES } from './trading-logic';
|
import { POINT_VALUES } from './trading-logic';
|
||||||
@@ -217,9 +217,14 @@ function connect() {
|
|||||||
s.ready = false;
|
s.ready = false;
|
||||||
|
|
||||||
socket.addEventListener('open', () => {
|
socket.addEventListener('open', () => {
|
||||||
|
// The stored uuid, once the master has issued one — it, not the name,
|
||||||
|
// is who this instance is, so renaming instance_name keeps the same
|
||||||
|
// person (and their accounting history) on the master.
|
||||||
|
const instanceId = getSetting('instance_id');
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
instance,
|
instance,
|
||||||
|
id: instanceId || undefined,
|
||||||
capabilities: collectCapabilities(),
|
capabilities: collectCapabilities(),
|
||||||
} satisfies InstanceMessage));
|
} satisfies InstanceMessage));
|
||||||
});
|
});
|
||||||
@@ -235,6 +240,9 @@ function connect() {
|
|||||||
if (msg.type === 'welcome') {
|
if (msg.type === 'welcome') {
|
||||||
s.ready = true;
|
s.ready = true;
|
||||||
s.attempt = 0;
|
s.attempt = 0;
|
||||||
|
if (msg.personId && msg.personId !== getSetting('instance_id')) {
|
||||||
|
setSetting('instance_id', msg.personId);
|
||||||
|
}
|
||||||
console.log(`[master-link] connected to ${url} as "${instance}"`);
|
console.log(`[master-link] connected to ${url} as "${instance}"`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,10 +106,13 @@ export type Command =
|
|||||||
| { kind: 'report-now' };
|
| { kind: 'report-now' };
|
||||||
|
|
||||||
export type MasterMessage =
|
export type MasterMessage =
|
||||||
| { type: 'welcome' }
|
/** personId is the person's stable uuid; the instance stores it and
|
||||||
|
* presents it on later hellos, which is what makes a rename follow the
|
||||||
|
* person. */
|
||||||
|
| { type: 'welcome'; personId?: string }
|
||||||
| { type: 'command'; id: string; command: Command };
|
| { type: 'command'; id: string; command: Command };
|
||||||
|
|
||||||
export type InstanceMessage =
|
export type InstanceMessage =
|
||||||
| { type: 'hello'; instance: string; capabilities: InstanceCapabilities }
|
| { type: 'hello'; instance: string; id?: string; capabilities: InstanceCapabilities }
|
||||||
| { type: 'report'; firms: FirmStats[]; scheduler: SchedulerState; capabilities?: InstanceCapabilities }
|
| { type: 'report'; firms: FirmStats[]; scheduler: SchedulerState; capabilities?: InstanceCapabilities }
|
||||||
| { type: 'ack'; id: string; ok: boolean; result?: unknown; error?: string };
|
| { type: 'ack'; id: string; ok: boolean; result?: unknown; error?: string };
|
||||||
|
|||||||
+8
-1
@@ -132,7 +132,14 @@ export async function sendReport(): Promise<void> {
|
|||||||
await fetch(new URL('/api/report', url), {
|
await fetch(new URL('/api/report', url), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ instance: instanceName, firms, scheduler }),
|
body: JSON.stringify({
|
||||||
|
instance: instanceName,
|
||||||
|
// Same identity the socket hello carries, so a rename resolves
|
||||||
|
// to the same person whichever transport the report takes.
|
||||||
|
id: getSetting('instance_id') || undefined,
|
||||||
|
firms,
|
||||||
|
scheduler,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// Master might be down — silently continue
|
// Master might be down — silently continue
|
||||||
|
|||||||
Reference in New Issue
Block a user