@ours.network/cli 2.0.4 → 2.1.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.
@@ -96,7 +96,7 @@ export declare const COMMAND_GROUPS: {
96
96
  readonly handler: "setup";
97
97
  readonly summary: "Write one or more safe daemon settings noninteractively.";
98
98
  readonly effects: "Writes a mode-0600 JSON config unless --dry-run is used. Unknown and secret fields already present are preserved and never printed.";
99
- readonly example: "ours config setup --port 3070 --state-dir /srv/ours-a --api-visibility owner";
99
+ readonly example: "ours config setup --port 3070 --state-dir /srv/ours-a --history-max-bytes 104857600 --api-visibility owner";
100
100
  };
101
101
  };
102
102
  };
package/dist/commands.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  IDENTITY_PREBIND_EXCEPTIONS,
5
5
  commandSpec,
6
6
  isCommandGroup
7
- } from "./chunk-QKDH6CJR.js";
7
+ } from "./chunk-GQHDQR6I.js";
8
8
  export {
9
9
  COMMAND_GROUPS,
10
10
  DESTRUCTIVE_OPERATIONS,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  setupConfig,
3
3
  showConfig
4
- } from "./chunk-DRQEV4SN.js";
4
+ } from "./chunk-3C5WAQAT.js";
5
5
  import "./chunk-6K2JBKHI.js";
6
6
  export {
7
7
  setupConfig,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  assertConfigNotDrifted,
3
3
  loadConfig
4
- } from "./chunk-FXKFSNKS.js";
4
+ } from "./chunk-DTWZF3ZG.js";
5
5
 
6
6
  // ../../src/internal/daemon.ts
7
7
  import * as fs2 from "node:fs";
@@ -204,7 +204,7 @@ async function releaseHeld(lock) {
204
204
  async function bootKernel() {
205
205
  const { lock, acquired } = await ensureLock();
206
206
  try {
207
- const { bootWrapper } = await import("./boot-HNJ7ZR5L.js");
207
+ const { bootWrapper } = await import("./boot-QLA3LXVX.js");
208
208
  await bootWrapper();
209
209
  } catch (error) {
210
210
  if (acquired) await releaseHeld(lock);
@@ -214,7 +214,7 @@ async function bootKernel() {
214
214
  async function startDaemon(opts = {}) {
215
215
  const { lock, acquired } = await ensureLock();
216
216
  try {
217
- const runtime = await import("./server-35QOYLY4.js");
217
+ const runtime = await import("./server-3PM3QOPD.js");
218
218
  const { onRuntimeLoaded, ...daemonOptions } = opts;
219
219
  onRuntimeLoaded?.();
220
220
  const handle = await runtime.startDaemon({
package/dist/help.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  helpRequestPath,
4
4
  renderHelp,
5
5
  renderTopHelp
6
- } from "./chunk-NNHFXFLS.js";
6
+ } from "./chunk-H7DX6EHZ.js";
7
7
  import "./chunk-MS4TDUZY.js";
8
8
  import "./chunk-6NFATG6P.js";
9
- import "./chunk-QKDH6CJR.js";
9
+ import "./chunk-GQHDQR6I.js";
10
10
  export {
11
11
  helpHint,
12
12
  helpRequestPath,
package/dist/lifecycle.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  startDaemonManaged,
11
11
  stopDaemonManaged,
12
12
  writeManagedRecord
13
- } from "./chunk-DZH2YEXT.js";
13
+ } from "./chunk-UHYUIHM2.js";
14
14
  import "./chunk-6K2JBKHI.js";
15
15
  export {
16
16
  DAEMON_LOG_FILE,
package/dist/main.js CHANGED
@@ -12,12 +12,12 @@ import {
12
12
  import {
13
13
  setupConfig,
14
14
  showConfig
15
- } from "./chunk-DRQEV4SN.js";
15
+ } from "./chunk-3C5WAQAT.js";
16
16
  import {
17
17
  helpHint,
18
18
  helpRequestPath,
19
19
  renderHelp
20
- } from "./chunk-NNHFXFLS.js";
20
+ } from "./chunk-H7DX6EHZ.js";
21
21
  import {
22
22
  invokeOperation,
23
23
  isOperationName,
@@ -37,14 +37,14 @@ import {
37
37
  IDENTITY_PREBIND_EXCEPTIONS,
38
38
  commandSpec,
39
39
  isCommandGroup
40
- } from "./chunk-QKDH6CJR.js";
40
+ } from "./chunk-GQHDQR6I.js";
41
41
  import {
42
42
  DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
43
43
  inspectDaemon,
44
44
  serveDaemon,
45
45
  startDaemonManaged,
46
46
  stopDaemonManaged
47
- } from "./chunk-DZH2YEXT.js";
47
+ } from "./chunk-UHYUIHM2.js";
48
48
  import {
49
49
  attachClient,
50
50
  defaultConfigPath,
@@ -55,7 +55,7 @@ import {
55
55
  import { existsSync, readFileSync } from "node:fs";
56
56
  import { homedir } from "node:os";
57
57
  import { join, resolve } from "node:path";
58
- var CLI_VERSION = false ? "0.0.0-dev" : "2.0.4";
58
+ var CLI_VERSION = false ? "0.0.0-dev" : "2.1.0";
59
59
  var COMMON_VALUES = /* @__PURE__ */ new Set(["--endpoint", "--port", "--state-dir", "--config", "--identity"]);
60
60
  var COMMON_BOOLEANS = /* @__PURE__ */ new Set(["--json", "--yes", "--help"]);
61
61
  var DAEMON_READINESS_TIMEOUT_ENV = "OURS_DAEMON_READINESS_TIMEOUT_MS";
@@ -164,7 +164,13 @@ async function runGrouped(group, args, io) {
164
164
  return 0;
165
165
  }
166
166
  async function runConfig(args, io) {
167
- const values = /* @__PURE__ */ new Set([...COMMON_VALUES, "--broker-url", "--gc-interval-ms", "--api-visibility"]);
167
+ const values = /* @__PURE__ */ new Set([
168
+ ...COMMON_VALUES,
169
+ "--broker-url",
170
+ "--gc-interval-ms",
171
+ "--history-max-bytes",
172
+ "--api-visibility"
173
+ ]);
168
174
  const flags = parseFlags(args, values, /* @__PURE__ */ new Set(["--json", "--dry-run", "--help"]));
169
175
  const action = flags.positionals[0];
170
176
  const spec = action === void 0 ? void 0 : commandSpec("config", action);
@@ -179,6 +185,9 @@ async function runConfig(args, io) {
179
185
  if (flags.values["--port"]) patch.port = parseInteger(flags.values["--port"], "--port", 1, 65535);
180
186
  if (flags.values["--state-dir"]) patch.stateDir = resolve(flags.values["--state-dir"]);
181
187
  if (flags.values["--gc-interval-ms"]) patch.gcIntervalMs = parseInteger(flags.values["--gc-interval-ms"], "--gc-interval-ms", 1);
188
+ if (flags.values["--history-max-bytes"]) {
189
+ patch.historyMaxBytes = parseInteger(flags.values["--history-max-bytes"], "--history-max-bytes", 0);
190
+ }
182
191
  if (flags.values["--api-visibility"]) {
183
192
  const visibility = flags.values["--api-visibility"];
184
193
  if (!["owner", "shared", "open"].includes(visibility)) throw new CliUsageError("--api-visibility must be owner, shared, or open");
@@ -4,6 +4,7 @@ import {
4
4
  CONFIG,
5
5
  FILE_SELECTION_CAP,
6
6
  GC_INTERVAL_MS,
7
+ HISTORY_MAX_BYTES,
7
8
  PORT,
8
9
  PROTOCOL_VERSION,
9
10
  STATE_DIR,
@@ -32,11 +33,13 @@ import {
32
33
  exportSigningSecret,
33
34
  findIdentityFile,
34
35
  getFileHistoryItem,
36
+ getHistoryStorageStatus,
35
37
  getMessageHistoryItem,
36
38
  getMessageHistorySummary,
37
39
  hashLeaseToken,
38
40
  identities,
39
41
  identityDir,
42
+ initializeHistoryRetention,
40
43
  isSelectableWireId,
41
44
  keyPath,
42
45
  leaseByToken,
@@ -47,10 +50,12 @@ import {
47
50
  listMessageHistory,
48
51
  log,
49
52
  mutatingTx,
53
+ noteHistoryStorageMutation,
50
54
  openHistory,
51
55
  outboundRemovalInFlight,
52
56
  persistBindings,
53
57
  pidDefinitelyDead,
58
+ pinHistoryItems,
54
59
  pinRegistrar,
55
60
  provisionIdentity,
56
61
  publishToBook,
@@ -77,12 +82,14 @@ import {
77
82
  sessionReaperIntervalMs,
78
83
  setNotifyHook,
79
84
  startupProgress,
85
+ stopHistoryRetention,
80
86
  structuredVoiceOutcome,
81
87
  sttStatus,
82
88
  takeUnreadFiles,
83
89
  takeUnreadMessages,
84
90
  tombstones,
85
91
  transcribeVoice,
92
+ unpinHistoryItems,
86
93
  unpublishFromBook,
87
94
  unreadSummary,
88
95
  validateName,
@@ -91,11 +98,11 @@ import {
91
98
  withScopeAsync,
92
99
  wrapper,
93
100
  writeTempMetaFile
94
- } from "./chunk-TSYICL3I.js";
101
+ } from "./chunk-YPDZQ4XD.js";
95
102
  import {
96
103
  buildIdentityFile,
97
104
  writeIdentityFile
98
- } from "./chunk-FXKFSNKS.js";
105
+ } from "./chunk-DTWZF3ZG.js";
99
106
 
100
107
  // ../../src/http/server.ts
101
108
  import { createServer as createHttpServer } from "node:http";
@@ -1467,8 +1474,11 @@ async function getFiles(ctx, a = {}) {
1467
1474
  if (wire_ids.some((wire) => !isSelectableWireId(wire))) throw errMalformedId();
1468
1475
  if (new Set(wire_ids).size !== wire_ids.length) throw errDuplicateId();
1469
1476
  }
1477
+ let pinned = [];
1470
1478
  try {
1471
1479
  const selected = takeUnreadFiles(id, { wire_ids, limit: a.limit });
1480
+ pinned = selected.files.map((file) => file.wire_id);
1481
+ pinHistoryItems(id.dir, "file", pinned);
1472
1482
  const files = [];
1473
1483
  const lines = [];
1474
1484
  for (const file of selected.files) {
@@ -1535,6 +1545,11 @@ ${lines.join("\n")}`,
1535
1545
  throw errUnknownOrStaleId();
1536
1546
  }
1537
1547
  throw errTool("get_files", message);
1548
+ } finally {
1549
+ if (pinned.length > 0) {
1550
+ unpinHistoryItems(id.dir, "file", pinned);
1551
+ noteHistoryStorageMutation(id.dir);
1552
+ }
1538
1553
  }
1539
1554
  }
1540
1555
  function saveFileFallbackNotice(ctx, a) {
@@ -1848,7 +1863,14 @@ function looksLikeInitializeRequest(body) {
1848
1863
  const m = body;
1849
1864
  return m.jsonrpc === "2.0" && m.method === "initialize";
1850
1865
  }
1851
- var LOAD_TIME_CONFIG_FIELDS = ["brokerUrl", "port", "stateDir", "gcIntervalMs", "apiVisibility"];
1866
+ var LOAD_TIME_CONFIG_FIELDS = [
1867
+ "brokerUrl",
1868
+ "port",
1869
+ "stateDir",
1870
+ "gcIntervalMs",
1871
+ "historyMaxBytes",
1872
+ "apiVisibility"
1873
+ ];
1852
1874
  function readBody(req) {
1853
1875
  return new Promise((resolve2, reject) => {
1854
1876
  let data = "";
@@ -1898,6 +1920,10 @@ async function startHttpDaemon(opts) {
1898
1920
  log("booting wrapper\u2026");
1899
1921
  try {
1900
1922
  await bootWrapper();
1923
+ const historyStorage = initializeHistoryRetention(STATE_DIR, HISTORY_MAX_BYTES);
1924
+ log(
1925
+ `history storage ${historyStorage.state} (physical=${historyStorage.physical_bytes}, cap=${historyStorage.max_bytes}, overflow=${historyStorage.overflow_bytes})`
1926
+ );
1901
1927
  startGcTimer();
1902
1928
  } catch (err) {
1903
1929
  if (installedHook) clearNotifyHook(installedHook);
@@ -2027,6 +2053,12 @@ async function startHttpDaemon(opts) {
2027
2053
  res.end(JSON.stringify(unreadSummary()));
2028
2054
  return;
2029
2055
  }
2056
+ if (req.method === "GET" && url.pathname === "/history-storage") {
2057
+ if (!requireAuth(req, res)) return;
2058
+ res.writeHead(200, { "Content-Type": "application/json" });
2059
+ res.end(JSON.stringify(getHistoryStorageStatus()));
2060
+ return;
2061
+ }
2030
2062
  {
2031
2063
  const m = /^\/identities\/([^/]+)\/notifications$/.exec(url.pathname);
2032
2064
  if (req.method === "GET" && m) {
@@ -2223,6 +2255,7 @@ async function startHttpDaemon(opts) {
2223
2255
  torn = true;
2224
2256
  clearInterval(sessionReaper);
2225
2257
  stopGcTimer();
2258
+ stopHistoryRetention();
2226
2259
  if (installedHook) clearNotifyHook(installedHook);
2227
2260
  for (const sid of Object.keys(transports)) {
2228
2261
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/cli",
3
- "version": "2.0.4",
3
+ "version": "2.1.0",
4
4
  "description": "Transport-neutral operator CLI for the ours shared daemon",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",