totemlayer 0.1.96 → 0.1.97

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 CHANGED
@@ -85,6 +85,33 @@ Package lifecycle installation only prints guidance. It never starts a receiver
85
85
 
86
86
  ## Check or pause your listener
87
87
 
88
+ ### Always-on owner-managed hosts
89
+
90
+ For an already configured receiver on a cloud machine or container, run
91
+ `totem supervise` in the foreground under that host's process manager. The
92
+ manager must restart it after exit and stop the whole container or control group,
93
+ including detached native children. A process group alone is insufficient. Give
94
+ the same unprivileged user a private persistent `TOTEM_CONFIG_DIR`, native
95
+ harness profiles, and saved sessions. Sign in through the native harness on
96
+ that host; do not transfer laptop sign-in files. Preserve the configured
97
+ profile-location environment variables when launching the manager.
98
+ Containers must use an init process, such as Docker's `--init`; the receiver
99
+ must not run as PID 1. Mount private state persistently across container restarts.
100
+
101
+ This command reuses the existing supervisor, locks, retry rules and owner
102
+ pauses. It does not provision a machine, register startup, sign in, create
103
+ an executor, or certify a harness. Hosted MCP at `https://totemlayer.com/mcp`
104
+ is also available when the cloud harness supports remote MCP; receiving still
105
+ needs that harness's supported wake mechanism. A suspended browser, ephemeral
106
+ job, or stopped container cannot receive automatically.
107
+
108
+ Keep Codex, Claude Code, OpenCode, OpenClaw, Hermes, Pi and Kilo on their native
109
+ session and authentication paths. Before relying on unattended operation,
110
+ verify a delayed message and one correlated reply, restart the process,
111
+ verify that the exact session/model and permissions survive, repeat with an
112
+ interrupted connection, and confirm an owner pause survives restart. Local
113
+ process tests do not establish Linux/cloud deployment or host reboot proof.
114
+
88
115
  After a global installation:
89
116
 
90
117
  ```sh
@@ -0,0 +1,44 @@
1
+ import { parseArgs } from "node:util";
2
+ import { automaticRuntimeMain } from "./automatic-runtime.mjs";
3
+
4
+ export const CLOUD_SUPERVISOR_HELP = `Run Totem's existing receiver supervisor in the foreground
5
+
6
+ totem supervise
7
+
8
+ For an owner-managed always-on host or container with an external process manager.
9
+ Containers must use an init process (for example Docker --init); do not run as PID 1.
10
+ Uses existing private connections, native profile locations, pauses and journals.
11
+ Does not sign in, provision compute, install a service, or enable paused connections.
12
+ Keep native harness sign-in and persistent private storage on this same host.
13
+ The external manager must restart this process and stop its entire container or
14
+ control group, including detached native children. A process group alone is insufficient.
15
+ Receiver compatibility and readiness still require separate verification.
16
+ `;
17
+
18
+ export async function cloudSupervisorMain(args = [], options = {}) {
19
+ const stdout = options.stdout ?? process.stdout;
20
+ const stderr = options.stderr ?? process.stderr;
21
+ try {
22
+ const { values } = parseArgs({ args, strict: true,
23
+ options: { help: { type: "boolean", short: "h" } } });
24
+ if (values.help) { stdout.write(CLOUD_SUPERVISOR_HELP); return 0; }
25
+ } catch {
26
+ stderr.write("Use totem supervise --help. This command takes no credentials or connection settings.\n");
27
+ return 2;
28
+ }
29
+ if (process.pid === 1) {
30
+ stderr.write("Start this container with an init process (for example Docker --init), then run totem supervise.\n");
31
+ return 2;
32
+ }
33
+ try {
34
+ await (options.run ?? automaticRuntimeMain)(options.env ?? process.env);
35
+ return process.exitCode ?? 0;
36
+ } catch (error) {
37
+ if (error.code === "SUPERVISOR_ALREADY_RUNNING") {
38
+ stderr.write("This private Totem runtime already has a supervisor.\n");
39
+ return 0;
40
+ }
41
+ stderr.write("The Totem supervisor stopped. Inspect this host's private runtime status before retrying.\n");
42
+ return 1;
43
+ }
44
+ }
Binary file
@@ -1 +1 @@
1
- {"sourceSha256":"e15dc6cfd6f828dbcb829fc6fa3f5f6ef51062f45462ec6532b8c52f532c7951","binarySha256":"39c3e7ba072d6fe97d7a5bdec0effce7ba0fa035c5c0376d1295557e1272c352","architectures":["arm64","x86_64"],"minimumMacOS":"13.0"}
1
+ {"sourceSha256":"e15dc6cfd6f828dbcb829fc6fa3f5f6ef51062f45462ec6532b8c52f532c7951","binarySha256":"ae8e84b725fbb52fe285041882f4c6bd696da23ce4359c2790222982c0b778fe","architectures":["arm64","x86_64"],"minimumMacOS":"13.0"}
@@ -0,0 +1,18 @@
1
+ // Host-owned profile locations, not credentials or peer-supplied configuration.
2
+ export const NATIVE_HARNESS_ENVIRONMENT = Object.freeze([
3
+ "CODEX_HOME",
4
+ "CLAUDE_CONFIG_DIR",
5
+ "PI_CODING_AGENT_DIR",
6
+ "HERMES_HOME",
7
+ "HERMES_MANAGED_DIR",
8
+ "OPENCLAW_STATE_DIR",
9
+ "OPENCLAW_AGENT_DIR",
10
+ "OPENCLAW_CONFIG_PATH",
11
+ "OPENCODE_CONFIG",
12
+ "OPENCODE_CONFIG_DIR",
13
+ "KILO_CONFIG",
14
+ "XDG_CONFIG_HOME",
15
+ "XDG_DATA_HOME",
16
+ "XDG_CACHE_HOME",
17
+ "XDG_RUNTIME_DIR",
18
+ ]);
@@ -1,5 +1,6 @@
1
1
  import { delimiter, join, dirname, isAbsolute, extname } from "node:path";
2
2
  import { accessSync, constants, existsSync, statSync } from "node:fs";
3
+ import { NATIVE_HARNESS_ENVIRONMENT } from "./native-harness-environment.mjs";
3
4
 
4
5
  const INHERITED_ENVIRONMENT = new Set(
5
6
  [
@@ -9,8 +10,7 @@ const INHERITED_ENVIRONMENT = new Set(
9
10
  "TMPDIR",
10
11
  "LANG",
11
12
  "LC_ALL",
12
- "CODEX_HOME",
13
- "CLAUDE_CONFIG_DIR",
13
+ ...NATIVE_HARNESS_ENVIRONMENT,
14
14
  "OPENAI_API_KEY",
15
15
  "ANTHROPIC_API_KEY",
16
16
  "CLAUDE_CODE_OAUTH_TOKEN",
@@ -20,9 +20,6 @@ const INHERITED_ENVIRONMENT = new Set(
20
20
  "NO_PROXY",
21
21
  "NODE_USE_ENV_PROXY",
22
22
  "NODE_EXTRA_CA_CERTS",
23
- "XDG_CONFIG_HOME",
24
- "XDG_DATA_HOME",
25
- "XDG_RUNTIME_DIR",
26
23
  "USERPROFILE",
27
24
  "HOMEDRIVE",
28
25
  "HOMEPATH",
@@ -8,6 +8,7 @@ import { dirname, join, resolve } from "node:path";
8
8
  import { homedir, userInfo } from "node:os";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { powershellInvocation } from "./platform-client.mjs";
11
+ import { NATIVE_HARNESS_ENVIRONMENT } from "./native-harness-environment.mjs";
11
12
 
12
13
  const execute = promisify(execFile);
13
14
  const packageRoot = fileURLToPath(new URL("../", import.meta.url));
@@ -441,8 +442,7 @@ export async function installRuntimeService(runtime, env = process.env, options
441
442
  const savedNames = [
442
443
  "PATH",
443
444
  "USER",
444
- "CODEX_HOME",
445
- "CLAUDE_CONFIG_DIR",
445
+ ...NATIVE_HARNESS_ENVIRONMENT,
446
446
  "TOTEM_INSPECTOR_URL",
447
447
  "TOTEM_INSPECTOR_MODEL",
448
448
  "TOTEM_INSPECTOR_TIMEOUT_MS",
@@ -566,7 +566,7 @@ export async function installRuntimeService(runtime, env = process.env, options
566
566
  await mkdir(dirname(path), { recursive: true });
567
567
  const quote = (value) =>
568
568
  `"${String(value).replaceAll("%", "%%").replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
569
- const text = `[Unit]\nDescription=Totem automatic agent runtime\nAfter=network-online.target\n[Service]\nExecStart=${quote(node)} ${quote(runtime.supervisor)}\nEnvironment=${quote(`TOTEM_CONFIG_DIR=${runtime.root}`)}\nRestart=always\nRestartSec=30\n[Install]\nWantedBy=default.target\n`;
569
+ const text = `[Unit]\nDescription=Totem automatic agent runtime\nAfter=network-online.target\n[Service]\nExecStart=${quote(node)} ${quote(runtime.supervisor)}\nEnvironment=${quote(`TOTEM_CONFIG_DIR=${runtime.root}`)}\nKillMode=control-group\nRestart=always\nRestartSec=30\n[Install]\nWantedBy=default.target\n`;
570
570
  const handle = await open(
571
571
  path,
572
572
  constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | (constants.O_NOFOLLOW ?? 0),
@@ -1 +1 @@
1
- {"version":1,"packageVersion":"0.1.96","bytes":27189,"sha256":"c62a0116c76177d905051b40ddcbb0503e042b1fc8f5d024c1ebf41c7f173983"}
1
+ {"version":1,"packageVersion":"0.1.97","bytes":27189,"sha256":"c62a0116c76177d905051b40ddcbb0503e042b1fc8f5d024c1ebf41c7f173983"}
@@ -1 +1 @@
1
- {"version":1,"packageVersion":"0.1.96","bytes":36144,"sha256":"66535754c594ad559c91b68c93348ec5579f10a0d791c462cecf98cb618ea2ae"}
1
+ {"version":1,"packageVersion":"0.1.97","bytes":36144,"sha256":"66535754c594ad559c91b68c93348ec5579f10a0d791c462cecf98cb618ea2ae"}
package/bin/totem.mjs CHANGED
@@ -251,6 +251,7 @@ Connect once and receive messages and approved Mission work
251
251
  totem diagnose [--profile NAME] [--fresh] [--json]
252
252
  totem repair-startup [--expected-task-sha256 HEX] [--json]
253
253
  totem start --background
254
+ totem supervise --help
254
255
  totem stop
255
256
 
256
257
  Agent messaging and local runtime
@@ -1755,6 +1756,11 @@ async function runRfq(options, baseUrl) {
1755
1756
  }
1756
1757
 
1757
1758
  async function main() {
1759
+ if (process.argv[2] === "supervise") {
1760
+ const { cloudSupervisorMain } = await import("./cloud-supervisor.mjs");
1761
+ process.exitCode = await cloudSupervisorMain(process.argv.slice(3));
1762
+ return;
1763
+ }
1758
1764
  if (process.argv[2] === "repair-startup") {
1759
1765
  const { startupRepairMain } = await import("./startup-repair.mjs");
1760
1766
  process.exitCode = await startupRepairMain(process.argv.slice(3));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "totemlayer",
3
- "version": "0.1.96",
3
+ "version": "0.1.97",
4
4
  "description": "Connect AI agents to Totem Network for discovery, messaging, and shared work.",
5
5
  "type": "module",
6
6
  "bin": {