very-happy-cli 0.2.156 → 0.2.157
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/dist/{AcpBackend-BHe1_F1h.cjs → AcpBackend-BymWX-LI.cjs} +2 -2
- package/dist/{AcpBackend-C3nc1u-s.mjs → AcpBackend-Cy18U7yw.mjs} +2 -2
- package/dist/{AcpSessionManager-BVxkQnNf.cjs → AcpSessionManager-BF7zBGAi.cjs} +1 -1
- package/dist/{AcpSessionManager-dN3k1LbI.mjs → AcpSessionManager-Ds8rcp95.mjs} +1 -1
- package/dist/{agentGuidance-C604n0Zq.mjs → agentGuidance-CU6YD_Ms.mjs} +560 -29
- package/dist/{agentGuidance-C-CmV03f.cjs → agentGuidance-Db76O1kw.cjs} +579 -28
- package/dist/{agentHome-CmIJNV7f.cjs → agentHome-B0CLy0Qx.cjs} +2 -2
- package/dist/{agentHome-D2XGsoqs.mjs → agentHome-BaJ9xtxB.mjs} +2 -2
- package/dist/{auto-B1I6G_Nf.mjs → auto-B1YAwaeU.mjs} +6 -6
- package/dist/{auto-CMDq4oIu.cjs → auto-DqKGFq8U.cjs} +6 -6
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-BYHSRY2E.cjs → config-CzkBjZnV.cjs} +4 -4
- package/dist/{config-BkTu25bH.mjs → config-aAbhJllb.mjs} +4 -4
- package/dist/{index-B5QbzEga.mjs → index-BVu8Cufk.mjs} +8 -8
- package/dist/{index-DAAreiRF.mjs → index-BYU1e81k.mjs} +288 -44
- package/dist/{index-BbtR-Qto.cjs → index-C9QzZsUQ.cjs} +291 -47
- package/dist/{index-B_-7NpGZ.cjs → index-CTbGqVFY.cjs} +7 -7
- package/dist/{index-CB8QnjvS.cjs → index-Cz-uqnJk.cjs} +4 -4
- package/dist/{index-PYO8Mdz4.mjs → index-DQcRkI9r.mjs} +4 -4
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{install-CO1ps6Wt.cjs → install-BMlV0Tnr.cjs} +1 -1
- package/dist/{install-CNtXzwT_.mjs → install-BuYnqtVa.mjs} +1 -1
- package/dist/{installTerminalHooks-BOAnQ1Qe.cjs → installTerminalHooks-BVVOEGhG.cjs} +4 -4
- package/dist/{installTerminalHooks-CMpOV3RU.mjs → installTerminalHooks-ELDoqh0A.mjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +77 -1
- package/dist/lib.d.mts +77 -1
- package/dist/lib.mjs +2 -2
- package/dist/{limits-DmMdchTz.mjs → limits-A2c-3M-_.mjs} +162 -11
- package/dist/{limits-o29KXBgj.cjs → limits-CyuIt-Vl.cjs} +169 -10
- package/dist/{mcp-C_uejotl.mjs → mcp-Bo934Kpy.mjs} +4 -4
- package/dist/{mcp-CwK1nI7Y.cjs → mcp-CXkvnztT.cjs} +4 -4
- package/dist/{piRuntime-Dj5WnqzJ.cjs → piRuntime-CJjA26oD.cjs} +2 -2
- package/dist/{piRuntime-Cu2-8nOT.mjs → piRuntime-DdEY2mE9.mjs} +2 -2
- package/dist/{piTerminal-D-BIbov9.cjs → piTerminal-CpU3hzrX.cjs} +6 -6
- package/dist/{piTerminal-DanZeBrO.mjs → piTerminal-DwWmgumi.mjs} +6 -6
- package/dist/{runGemini-CZ_lhMWY.mjs → runGemini-Cj6oJQ-I.mjs} +6 -6
- package/dist/{runGemini--QqK-Ap5.cjs → runGemini-DS-0qm0L.cjs} +6 -6
- package/dist/{runOpenClaw-C0xLhTvk.cjs → runOpenClaw-CvSm8oTc.cjs} +5 -5
- package/dist/{runOpenClaw-CxgLpm38.mjs → runOpenClaw-DUCQxkI-.mjs} +5 -5
- package/dist/{send-CTcGMVuG.cjs → send-CHxobaoI.cjs} +43 -31
- package/dist/{send-BaOkDgH0.mjs → send-D5CyRyZ_.mjs} +43 -31
- package/dist/{sessions-OmvyJb0n.cjs → sessions-BFlIrKCY.cjs} +133 -49
- package/dist/{sessions-Bn304xF1.mjs → sessions-DsE01xKc.mjs} +133 -49
- package/dist/{spawn-BKNabutV.mjs → spawn-CdihhwHs.mjs} +4 -4
- package/dist/{spawn-CC6V2KiA.cjs → spawn-HV345RN-.cjs} +4 -4
- package/dist/{teams-CLbS2_4Q.mjs → teams-BE8nMB6O.mjs} +5 -5
- package/dist/{teams-XtrH3_h3.cjs → teams-BahinMnH.cjs} +5 -5
- package/dist/{todo-CEDvKNXK.cjs → todo-CMMQ6yhU.cjs} +2 -2
- package/dist/{todo-BcPbNKt2.mjs → todo-CuQAkSp2.mjs} +2 -2
- package/dist/{types-CphWjqHc.cjs → types-B0sSi6eM.cjs} +77 -4
- package/dist/{types-BEVzYSoV.mjs → types-BrGJ7PJz.mjs} +77 -4
- package/package.json +7 -7
|
@@ -4,12 +4,12 @@ var ink = require('ink');
|
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
6
|
var path = require('node:path');
|
|
7
|
-
var index$1 = require('./types-
|
|
8
|
-
var persistence = require('./limits-
|
|
9
|
-
var index = require('./index-
|
|
7
|
+
var index$1 = require('./types-B0sSi6eM.cjs');
|
|
8
|
+
var persistence = require('./limits-CyuIt-Vl.cjs');
|
|
9
|
+
var index = require('./index-C9QzZsUQ.cjs');
|
|
10
10
|
var terminalTitleSuggest = require('./terminalTitleSuggest-Dji8I0Jy.cjs');
|
|
11
|
-
var AcpBackend = require('./AcpBackend-
|
|
12
|
-
var config = require('./config-
|
|
11
|
+
var AcpBackend = require('./AcpBackend-BymWX-LI.cjs');
|
|
12
|
+
var config = require('./config-CzkBjZnV.cjs');
|
|
13
13
|
require('axios');
|
|
14
14
|
require('node:events');
|
|
15
15
|
require('socket.io-client');
|
|
@@ -34,7 +34,7 @@ require('@anthropic-ai/sandbox-runtime');
|
|
|
34
34
|
require('expo-server-sdk');
|
|
35
35
|
require('chalk');
|
|
36
36
|
require('node:util');
|
|
37
|
-
require('./agentGuidance-
|
|
37
|
+
require('./agentGuidance-Db76O1kw.cjs');
|
|
38
38
|
require('@anthropic-ai/claude-agent-sdk');
|
|
39
39
|
require('ps-list');
|
|
40
40
|
require('tmp');
|
|
@@ -5,10 +5,10 @@ var node_child_process = require('node:child_process');
|
|
|
5
5
|
var os = require('node:os');
|
|
6
6
|
var node_fs = require('node:fs');
|
|
7
7
|
var path = require('node:path');
|
|
8
|
-
var index = require('./types-
|
|
9
|
-
var AcpSessionManager = require('./AcpSessionManager-
|
|
10
|
-
var persistence = require('./limits-
|
|
11
|
-
var index$1 = require('./index-
|
|
8
|
+
var index = require('./types-B0sSi6eM.cjs');
|
|
9
|
+
var AcpSessionManager = require('./AcpSessionManager-BF7zBGAi.cjs');
|
|
10
|
+
var persistence = require('./limits-CyuIt-Vl.cjs');
|
|
11
|
+
var index$1 = require('./index-C9QzZsUQ.cjs');
|
|
12
12
|
var WebSocket = require('ws');
|
|
13
13
|
var ed = require('@noble/ed25519');
|
|
14
14
|
var sha2_js = require('@noble/hashes/sha2.js');
|
|
@@ -35,7 +35,7 @@ require('@anthropic-ai/sandbox-runtime');
|
|
|
35
35
|
require('expo-server-sdk');
|
|
36
36
|
require('chalk');
|
|
37
37
|
require('node:util');
|
|
38
|
-
require('./agentGuidance-
|
|
38
|
+
require('./agentGuidance-Db76O1kw.cjs');
|
|
39
39
|
require('ink');
|
|
40
40
|
require('react');
|
|
41
41
|
require('@anthropic-ai/claude-agent-sdk');
|
|
@@ -3,10 +3,10 @@ import { execFileSync } from 'node:child_process';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import { i as errorLogMetadata, j as contentLogMetadata, h as connectionState, A as ApiClient } from './types-
|
|
7
|
-
import { A as AcpSessionManager } from './AcpSessionManager-
|
|
8
|
-
import { z as ensurePrivateDirectorySync, D as hardenPrivateFileSync, E as writePrivateFileSync, r as readSettings, v as notifyDaemonSessionStarted, w as encodeBase64, l as logger, c as configuration } from './limits-
|
|
9
|
-
import { h as initialMachineMetadata, j as createSessionMetadata, k as setupOfflineReconnection, M as MessageQueue2, v as registerKillSessionHandler } from './index-
|
|
6
|
+
import { i as errorLogMetadata, j as contentLogMetadata, h as connectionState, A as ApiClient } from './types-BrGJ7PJz.mjs';
|
|
7
|
+
import { A as AcpSessionManager } from './AcpSessionManager-Ds8rcp95.mjs';
|
|
8
|
+
import { z as ensurePrivateDirectorySync, D as hardenPrivateFileSync, E as writePrivateFileSync, r as readSettings, v as notifyDaemonSessionStarted, w as encodeBase64, l as logger, c as configuration } from './limits-A2c-3M-_.mjs';
|
|
9
|
+
import { h as initialMachineMetadata, j as createSessionMetadata, k as setupOfflineReconnection, M as MessageQueue2, v as registerKillSessionHandler } from './index-BYU1e81k.mjs';
|
|
10
10
|
import WebSocket from 'ws';
|
|
11
11
|
import * as ed from '@noble/ed25519';
|
|
12
12
|
import { sha512 } from '@noble/hashes/sha2.js';
|
|
@@ -33,7 +33,7 @@ import '@anthropic-ai/sandbox-runtime';
|
|
|
33
33
|
import 'expo-server-sdk';
|
|
34
34
|
import 'chalk';
|
|
35
35
|
import 'node:util';
|
|
36
|
-
import './agentGuidance-
|
|
36
|
+
import './agentGuidance-CU6YD_Ms.mjs';
|
|
37
37
|
import 'ink';
|
|
38
38
|
import 'react';
|
|
39
39
|
import '@anthropic-ai/claude-agent-sdk';
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var node_fs = require('node:fs');
|
|
5
5
|
var path = require('node:path');
|
|
6
|
-
var persistence = require('./limits-
|
|
7
|
-
var agentGuidance = require('./agentGuidance-
|
|
6
|
+
var persistence = require('./limits-CyuIt-Vl.cjs');
|
|
7
|
+
var agentGuidance = require('./agentGuidance-Db76O1kw.cjs');
|
|
8
8
|
require('zod');
|
|
9
9
|
require('@paralleldrive/cuid2');
|
|
10
10
|
require('node:fs/promises');
|
|
@@ -16,6 +16,7 @@ require('axios');
|
|
|
16
16
|
require('socket.io-client');
|
|
17
17
|
|
|
18
18
|
const EXIT_SESSION_NOT_LIVE = 3;
|
|
19
|
+
const MACHINE_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
19
20
|
function parseSendArgs(args) {
|
|
20
21
|
const options = { resume: false, json: false, help: false };
|
|
21
22
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -38,6 +39,10 @@ function parseSendArgs(args) {
|
|
|
38
39
|
options.model = value;
|
|
39
40
|
} else if (arg === "--resume") {
|
|
40
41
|
options.resume = true;
|
|
42
|
+
} else if (arg === "--machine") {
|
|
43
|
+
const value = args[++i];
|
|
44
|
+
if (value === void 0 || !MACHINE_ID_RE.test(value)) throw new Error("--machine requires a machine id");
|
|
45
|
+
options.machine = value;
|
|
41
46
|
} else if (arg === "--json") {
|
|
42
47
|
options.json = true;
|
|
43
48
|
} else if (arg === "--help" || arg === "-h") {
|
|
@@ -57,7 +62,7 @@ ${chalk.bold("very-happy send")} - Send a message into an existing session (for
|
|
|
57
62
|
|
|
58
63
|
${chalk.bold("Usage:")}
|
|
59
64
|
very-happy send --session <id> (--prompt <text> | --prompt-file <file>)
|
|
60
|
-
[--model <id>] [--resume] [--json]
|
|
65
|
+
[--model <id>] [--resume] [--machine <id>] [--json]
|
|
61
66
|
|
|
62
67
|
${chalk.bold("Options:")}
|
|
63
68
|
--session, -s <id> Target session id (required)
|
|
@@ -67,24 +72,30 @@ ${chalk.bold("Options:")}
|
|
|
67
72
|
('default' = the machine default). Without it the
|
|
68
73
|
session keeps whatever model it is on.
|
|
69
74
|
--resume If the session is archived or offline, bring it back
|
|
70
|
-
on
|
|
75
|
+
on its machine first (same as the web's Restore),
|
|
71
76
|
wait until it is live, then send.
|
|
77
|
+
--machine <id> The machine the session runs on. Skips the lookup
|
|
78
|
+
across the account's machines and always goes
|
|
79
|
+
through that machine's daemon.
|
|
72
80
|
--json Machine-readable output (see below)
|
|
73
81
|
-h, --help Show this help
|
|
74
82
|
|
|
75
83
|
${chalk.bold("Behavior:")}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
A session spawned by THIS machine's daemon is sent to directly (its key is
|
|
85
|
+
in ~/.happy/sessions.json). Any other session of the account is sent
|
|
86
|
+
through the daemon of the machine that spawned it: that machine must be
|
|
87
|
+
online and on CLI \u2265 0.2.157; the CLI here never sees its key. Without
|
|
88
|
+
--machine the online machines are asked in turn which one holds it.
|
|
79
89
|
|
|
80
|
-
Before sending, the session is classified from
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
Before sending, the session is classified from its daemon and the server:
|
|
91
|
+
live (a wrapper is attached), archived, offline (wrapper exited / presence
|
|
92
|
+
timed out) or not_found. The server stores messages for any session, so
|
|
93
|
+
only "live" means someone will read it; anything else is refused with exit
|
|
94
|
+
3 unless --resume is given (a remote session is resumed on ITS machine).
|
|
85
95
|
|
|
86
96
|
${chalk.bold("JSON output:")}
|
|
87
|
-
delivered {"sessionId","url","delivered":true,"status":"live","resumed":false|true
|
|
97
|
+
delivered {"sessionId","url","delivered":true,"status":"live","resumed":false|true,
|
|
98
|
+
"machine":{"id","host"}} (machine only when proxied)
|
|
88
99
|
refused {"sessionId","url","delivered":false,"status":"archived"|"offline"|"not_found",
|
|
89
100
|
"error":"...","resume":{"ok":false,"error":"..."}} (resume only with --resume)
|
|
90
101
|
dropped {"delivered":false,"stored":true,...} the POST succeeded but the wrapper
|
|
@@ -92,7 +103,8 @@ ${chalk.bold("JSON output:")}
|
|
|
92
103
|
|
|
93
104
|
${chalk.bold("Exit codes:")}
|
|
94
105
|
0 message delivered to a live wrapper
|
|
95
|
-
1 bad arguments, unknown session, or
|
|
106
|
+
1 bad arguments, unknown session (no machine of the account holds it, or
|
|
107
|
+
its machine is offline), or transport failure
|
|
96
108
|
3 session not live (archived / offline / not found) or resume failed
|
|
97
109
|
`);
|
|
98
110
|
}
|
|
@@ -144,24 +156,23 @@ async function handleSendCommand(args) {
|
|
|
144
156
|
if (prompt.trim().length === 0) {
|
|
145
157
|
fail(options, sessionId, "Prompt is empty");
|
|
146
158
|
}
|
|
147
|
-
const persisted = persistence.readPersistedSessions()[sessionId];
|
|
148
|
-
|
|
149
|
-
fail(
|
|
150
|
-
options,
|
|
151
|
-
sessionId,
|
|
152
|
-
`Session ${sessionId} has no encryption key in ${persistence.configuration.sessionsFile}. It was not spawned by this machine's daemon, or the daemon is too old to persist session keys.`
|
|
153
|
-
);
|
|
154
|
-
}
|
|
159
|
+
const persisted = options.machine ? void 0 : persistence.readPersistedSessions()[sessionId];
|
|
160
|
+
const model = options.model !== void 0 ? { model: options.model === "default" ? null : options.model } : {};
|
|
155
161
|
let result;
|
|
162
|
+
let machine;
|
|
156
163
|
try {
|
|
157
|
-
|
|
158
|
-
resume: options.resume,
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
if (persisted) {
|
|
165
|
+
result = await agentGuidance.deliverToSession(sessionId, persisted, prompt, "cli-send", { resume: options.resume, ...model });
|
|
166
|
+
} else {
|
|
167
|
+
const remote = await agentGuidance.sendRemoteMessage(sessionId, prompt, { resume: options.resume, ...model, ...options.machine ? { machineId: options.machine } : {} });
|
|
168
|
+
result = remote;
|
|
169
|
+
machine = remote.machine;
|
|
170
|
+
}
|
|
161
171
|
} catch (error) {
|
|
162
172
|
fail(options, sessionId, `Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
|
|
163
173
|
}
|
|
164
174
|
const url = agentGuidance.sessionWebUrl(sessionId);
|
|
175
|
+
const where = machine ? ` on ${machine.host}` : "";
|
|
165
176
|
if (!result.delivered) {
|
|
166
177
|
if (options.json) {
|
|
167
178
|
console.log(JSON.stringify({
|
|
@@ -172,20 +183,21 @@ async function handleSendCommand(args) {
|
|
|
172
183
|
resumed: result.resumed,
|
|
173
184
|
stored: result.stored,
|
|
174
185
|
error: result.error,
|
|
175
|
-
...result.resume ? { resume: result.resume } : {}
|
|
186
|
+
...result.resume ? { resume: result.resume } : {},
|
|
187
|
+
...machine ? { machine } : {}
|
|
176
188
|
}));
|
|
177
189
|
} else {
|
|
178
|
-
console.error(chalk.red("Not delivered:"), result.error ?? `session is ${result.status}`);
|
|
190
|
+
console.error(chalk.red("Not delivered:"), result.error ?? `session${where} is ${result.status}`);
|
|
179
191
|
if (result.stored) console.error(chalk.yellow("The message is stored server-side; nothing is reading it."));
|
|
180
192
|
}
|
|
181
193
|
process.exit(EXIT_SESSION_NOT_LIVE);
|
|
182
194
|
}
|
|
183
195
|
if (options.json) {
|
|
184
|
-
console.log(JSON.stringify({ sessionId, url, delivered: true, status: "live", resumed: result.resumed }));
|
|
196
|
+
console.log(JSON.stringify({ sessionId, url, delivered: true, status: "live", resumed: result.resumed, ...machine ? { machine } : {} }));
|
|
185
197
|
} else {
|
|
186
|
-
console.log(`${chalk.bold("Session:")} ${sessionId}`);
|
|
198
|
+
console.log(`${chalk.bold("Session:")} ${sessionId}${machine ? chalk.dim(` (machine ${machine.host})`) : ""}`);
|
|
187
199
|
console.log(`${chalk.bold("URL:")} ${url}`);
|
|
188
|
-
if (result.resumed) console.log(chalk.green(
|
|
200
|
+
if (result.resumed) console.log(chalk.green(`Session resumed on ${machine ? machine.host : "this machine"}.`));
|
|
189
201
|
console.log(chalk.green("Message sent."));
|
|
190
202
|
}
|
|
191
203
|
process.exit(0);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
-
import { d as readPersistedSessions
|
|
5
|
-
import {
|
|
4
|
+
import { d as readPersistedSessions } from './limits-A2c-3M-_.mjs';
|
|
5
|
+
import { u as deliverToSession, v as sendRemoteMessage, x as sessionWebUrl } from './agentGuidance-CU6YD_Ms.mjs';
|
|
6
6
|
import 'zod';
|
|
7
7
|
import '@paralleldrive/cuid2';
|
|
8
8
|
import 'node:fs/promises';
|
|
@@ -14,6 +14,7 @@ import 'axios';
|
|
|
14
14
|
import 'socket.io-client';
|
|
15
15
|
|
|
16
16
|
const EXIT_SESSION_NOT_LIVE = 3;
|
|
17
|
+
const MACHINE_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
17
18
|
function parseSendArgs(args) {
|
|
18
19
|
const options = { resume: false, json: false, help: false };
|
|
19
20
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -36,6 +37,10 @@ function parseSendArgs(args) {
|
|
|
36
37
|
options.model = value;
|
|
37
38
|
} else if (arg === "--resume") {
|
|
38
39
|
options.resume = true;
|
|
40
|
+
} else if (arg === "--machine") {
|
|
41
|
+
const value = args[++i];
|
|
42
|
+
if (value === void 0 || !MACHINE_ID_RE.test(value)) throw new Error("--machine requires a machine id");
|
|
43
|
+
options.machine = value;
|
|
39
44
|
} else if (arg === "--json") {
|
|
40
45
|
options.json = true;
|
|
41
46
|
} else if (arg === "--help" || arg === "-h") {
|
|
@@ -55,7 +60,7 @@ ${chalk.bold("very-happy send")} - Send a message into an existing session (for
|
|
|
55
60
|
|
|
56
61
|
${chalk.bold("Usage:")}
|
|
57
62
|
very-happy send --session <id> (--prompt <text> | --prompt-file <file>)
|
|
58
|
-
[--model <id>] [--resume] [--json]
|
|
63
|
+
[--model <id>] [--resume] [--machine <id>] [--json]
|
|
59
64
|
|
|
60
65
|
${chalk.bold("Options:")}
|
|
61
66
|
--session, -s <id> Target session id (required)
|
|
@@ -65,24 +70,30 @@ ${chalk.bold("Options:")}
|
|
|
65
70
|
('default' = the machine default). Without it the
|
|
66
71
|
session keeps whatever model it is on.
|
|
67
72
|
--resume If the session is archived or offline, bring it back
|
|
68
|
-
on
|
|
73
|
+
on its machine first (same as the web's Restore),
|
|
69
74
|
wait until it is live, then send.
|
|
75
|
+
--machine <id> The machine the session runs on. Skips the lookup
|
|
76
|
+
across the account's machines and always goes
|
|
77
|
+
through that machine's daemon.
|
|
70
78
|
--json Machine-readable output (see below)
|
|
71
79
|
-h, --help Show this help
|
|
72
80
|
|
|
73
81
|
${chalk.bold("Behavior:")}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
A session spawned by THIS machine's daemon is sent to directly (its key is
|
|
83
|
+
in ~/.happy/sessions.json). Any other session of the account is sent
|
|
84
|
+
through the daemon of the machine that spawned it: that machine must be
|
|
85
|
+
online and on CLI \u2265 0.2.157; the CLI here never sees its key. Without
|
|
86
|
+
--machine the online machines are asked in turn which one holds it.
|
|
77
87
|
|
|
78
|
-
Before sending, the session is classified from
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
Before sending, the session is classified from its daemon and the server:
|
|
89
|
+
live (a wrapper is attached), archived, offline (wrapper exited / presence
|
|
90
|
+
timed out) or not_found. The server stores messages for any session, so
|
|
91
|
+
only "live" means someone will read it; anything else is refused with exit
|
|
92
|
+
3 unless --resume is given (a remote session is resumed on ITS machine).
|
|
83
93
|
|
|
84
94
|
${chalk.bold("JSON output:")}
|
|
85
|
-
delivered {"sessionId","url","delivered":true,"status":"live","resumed":false|true
|
|
95
|
+
delivered {"sessionId","url","delivered":true,"status":"live","resumed":false|true,
|
|
96
|
+
"machine":{"id","host"}} (machine only when proxied)
|
|
86
97
|
refused {"sessionId","url","delivered":false,"status":"archived"|"offline"|"not_found",
|
|
87
98
|
"error":"...","resume":{"ok":false,"error":"..."}} (resume only with --resume)
|
|
88
99
|
dropped {"delivered":false,"stored":true,...} the POST succeeded but the wrapper
|
|
@@ -90,7 +101,8 @@ ${chalk.bold("JSON output:")}
|
|
|
90
101
|
|
|
91
102
|
${chalk.bold("Exit codes:")}
|
|
92
103
|
0 message delivered to a live wrapper
|
|
93
|
-
1 bad arguments, unknown session, or
|
|
104
|
+
1 bad arguments, unknown session (no machine of the account holds it, or
|
|
105
|
+
its machine is offline), or transport failure
|
|
94
106
|
3 session not live (archived / offline / not found) or resume failed
|
|
95
107
|
`);
|
|
96
108
|
}
|
|
@@ -142,24 +154,23 @@ async function handleSendCommand(args) {
|
|
|
142
154
|
if (prompt.trim().length === 0) {
|
|
143
155
|
fail(options, sessionId, "Prompt is empty");
|
|
144
156
|
}
|
|
145
|
-
const persisted = readPersistedSessions()[sessionId];
|
|
146
|
-
|
|
147
|
-
fail(
|
|
148
|
-
options,
|
|
149
|
-
sessionId,
|
|
150
|
-
`Session ${sessionId} has no encryption key in ${configuration.sessionsFile}. It was not spawned by this machine's daemon, or the daemon is too old to persist session keys.`
|
|
151
|
-
);
|
|
152
|
-
}
|
|
157
|
+
const persisted = options.machine ? void 0 : readPersistedSessions()[sessionId];
|
|
158
|
+
const model = options.model !== void 0 ? { model: options.model === "default" ? null : options.model } : {};
|
|
153
159
|
let result;
|
|
160
|
+
let machine;
|
|
154
161
|
try {
|
|
155
|
-
|
|
156
|
-
resume: options.resume,
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
if (persisted) {
|
|
163
|
+
result = await deliverToSession(sessionId, persisted, prompt, "cli-send", { resume: options.resume, ...model });
|
|
164
|
+
} else {
|
|
165
|
+
const remote = await sendRemoteMessage(sessionId, prompt, { resume: options.resume, ...model, ...options.machine ? { machineId: options.machine } : {} });
|
|
166
|
+
result = remote;
|
|
167
|
+
machine = remote.machine;
|
|
168
|
+
}
|
|
159
169
|
} catch (error) {
|
|
160
170
|
fail(options, sessionId, `Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
|
|
161
171
|
}
|
|
162
172
|
const url = sessionWebUrl(sessionId);
|
|
173
|
+
const where = machine ? ` on ${machine.host}` : "";
|
|
163
174
|
if (!result.delivered) {
|
|
164
175
|
if (options.json) {
|
|
165
176
|
console.log(JSON.stringify({
|
|
@@ -170,20 +181,21 @@ async function handleSendCommand(args) {
|
|
|
170
181
|
resumed: result.resumed,
|
|
171
182
|
stored: result.stored,
|
|
172
183
|
error: result.error,
|
|
173
|
-
...result.resume ? { resume: result.resume } : {}
|
|
184
|
+
...result.resume ? { resume: result.resume } : {},
|
|
185
|
+
...machine ? { machine } : {}
|
|
174
186
|
}));
|
|
175
187
|
} else {
|
|
176
|
-
console.error(chalk.red("Not delivered:"), result.error ?? `session is ${result.status}`);
|
|
188
|
+
console.error(chalk.red("Not delivered:"), result.error ?? `session${where} is ${result.status}`);
|
|
177
189
|
if (result.stored) console.error(chalk.yellow("The message is stored server-side; nothing is reading it."));
|
|
178
190
|
}
|
|
179
191
|
process.exit(EXIT_SESSION_NOT_LIVE);
|
|
180
192
|
}
|
|
181
193
|
if (options.json) {
|
|
182
|
-
console.log(JSON.stringify({ sessionId, url, delivered: true, status: "live", resumed: result.resumed }));
|
|
194
|
+
console.log(JSON.stringify({ sessionId, url, delivered: true, status: "live", resumed: result.resumed, ...machine ? { machine } : {} }));
|
|
183
195
|
} else {
|
|
184
|
-
console.log(`${chalk.bold("Session:")} ${sessionId}`);
|
|
196
|
+
console.log(`${chalk.bold("Session:")} ${sessionId}${machine ? chalk.dim(` (machine ${machine.host})`) : ""}`);
|
|
185
197
|
console.log(`${chalk.bold("URL:")} ${url}`);
|
|
186
|
-
if (result.resumed) console.log(chalk.green(
|
|
198
|
+
if (result.resumed) console.log(chalk.green(`Session resumed on ${machine ? machine.host : "this machine"}.`));
|
|
187
199
|
console.log(chalk.green("Message sent."));
|
|
188
200
|
}
|
|
189
201
|
process.exit(0);
|