agent-remnote 1.0.0 → 1.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/main.js +1783 -908
  3. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -74062,6 +74062,7 @@ function resolveStaleMs(explicit) {
74062
74062
  // src/lib/remnote.ts
74063
74063
  import fs2 from "node:fs";
74064
74064
  import path5 from "node:path";
74065
+ var SECONDARY_WORKSPACE_DIRS = new Set(["browser", "remnote-browser", "lnotes"]);
74065
74066
  function tryParseRemnoteLink(input) {
74066
74067
  const raw4 = input.trim();
74067
74068
  let u;
@@ -74107,6 +74108,47 @@ function tryParseRemnoteLinkFromRef(input) {
74107
74108
  function remnoteDbPathForWorkspaceId(workspaceId) {
74108
74109
  return path5.join(homeDir(), "remnote", `remnote-${workspaceId}`, "remnote.db");
74109
74110
  }
74111
+ function discoverWorkspaceCandidatesSync(baseDir = path5.join(homeDir(), "remnote")) {
74112
+ let entries2;
74113
+ try {
74114
+ entries2 = fs2.readdirSync(baseDir, { withFileTypes: true });
74115
+ } catch {
74116
+ return [];
74117
+ }
74118
+ const out = [];
74119
+ for (const entry of entries2) {
74120
+ if (!entry.isDirectory() || entry.name === "backups")
74121
+ continue;
74122
+ const isPrimary = entry.name.startsWith("remnote-");
74123
+ const isSecondary = SECONDARY_WORKSPACE_DIRS.has(entry.name);
74124
+ if (!isPrimary && !isSecondary)
74125
+ continue;
74126
+ const workspaceId = isPrimary ? entry.name.slice("remnote-".length).trim() : entry.name.trim();
74127
+ if (!workspaceId)
74128
+ continue;
74129
+ const dbPath = path5.join(baseDir, entry.name, "remnote.db");
74130
+ try {
74131
+ const stat3 = fs2.statSync(dbPath);
74132
+ if (!stat3.isFile())
74133
+ continue;
74134
+ out.push({
74135
+ workspaceId,
74136
+ dbPath,
74137
+ kind: isPrimary ? "primary" : "secondary",
74138
+ dirName: entry.name,
74139
+ mtimeMs: Number(stat3.mtimeMs ?? 0)
74140
+ });
74141
+ } catch {
74142
+ continue;
74143
+ }
74144
+ }
74145
+ out.sort((a, b) => {
74146
+ if (a.kind !== b.kind)
74147
+ return a.kind === "primary" ? -1 : 1;
74148
+ return b.mtimeMs - a.mtimeMs;
74149
+ });
74150
+ return out;
74151
+ }
74110
74152
  function tryResolveRemnoteDbPathForWorkspaceIdSync(workspaceId) {
74111
74153
  const p3 = remnoteDbPathForWorkspaceId(workspaceId);
74112
74154
  try {
@@ -75915,8 +75957,38 @@ var migration5 = {
75915
75957
  }
75916
75958
  };
75917
75959
 
75960
+ // src/internal/store/migrations/0006-add-workspace-bindings.ts
75961
+ var WORKSPACE_BINDINGS_TABLE_SQL = `CREATE TABLE IF NOT EXISTS workspace_bindings (
75962
+ workspace_id TEXT PRIMARY KEY,
75963
+ kb_name TEXT,
75964
+ db_path TEXT NOT NULL,
75965
+ source TEXT NOT NULL CHECK (source IN ('explicit','live_ui_context','single_candidate_auto','deep_link')),
75966
+ is_current INTEGER NOT NULL DEFAULT 0 CHECK (is_current IN (0, 1)),
75967
+ first_seen_at INTEGER NOT NULL,
75968
+ last_verified_at INTEGER NOT NULL,
75969
+ last_ui_context_at INTEGER,
75970
+ updated_at INTEGER NOT NULL
75971
+ );`;
75972
+ var WORKSPACE_BINDINGS_CURRENT_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS uq_workspace_bindings_current ON workspace_bindings(is_current) WHERE is_current = 1;`;
75973
+ var WORKSPACE_BINDINGS_UPDATED_AT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS idx_workspace_bindings_updated_at ON workspace_bindings(updated_at DESC);`;
75974
+ var WORKSPACE_BINDINGS_MIGRATION_SQL = [
75975
+ WORKSPACE_BINDINGS_TABLE_SQL,
75976
+ WORKSPACE_BINDINGS_CURRENT_INDEX_SQL,
75977
+ WORKSPACE_BINDINGS_UPDATED_AT_INDEX_SQL
75978
+ ];
75979
+ var migration6 = {
75980
+ version: 6,
75981
+ name: "add_workspace_bindings",
75982
+ checksumInput: WORKSPACE_BINDINGS_MIGRATION_SQL.join(`
75983
+ `),
75984
+ apply: (db) => {
75985
+ db.exec(WORKSPACE_BINDINGS_MIGRATION_SQL.join(`
75986
+ `));
75987
+ }
75988
+ };
75989
+
75918
75990
  // src/internal/store/migrations/index.ts
75919
- var migrationSpecs = [migration, migration2, migration3, migration4, migration5];
75991
+ var migrationSpecs = [migration, migration2, migration3, migration4, migration5, migration6];
75920
75992
 
75921
75993
  // src/internal/store/db.ts
75922
75994
  class StoreSchemaError extends Error {
@@ -76037,6 +76109,25 @@ CREATE TABLE IF NOT EXISTS queue_consumers (
76037
76109
  last_seen_at INTEGER NOT NULL,
76038
76110
  meta_json TEXT NOT NULL DEFAULT '{}'
76039
76111
  );
76112
+
76113
+ CREATE TABLE IF NOT EXISTS workspace_bindings (
76114
+ workspace_id TEXT PRIMARY KEY,
76115
+ kb_name TEXT,
76116
+ db_path TEXT NOT NULL,
76117
+ source TEXT NOT NULL CHECK (source IN ('explicit','live_ui_context','single_candidate_auto','deep_link')),
76118
+ is_current INTEGER NOT NULL DEFAULT 0 CHECK (is_current IN (0, 1)),
76119
+ first_seen_at INTEGER NOT NULL,
76120
+ last_verified_at INTEGER NOT NULL,
76121
+ last_ui_context_at INTEGER,
76122
+ updated_at INTEGER NOT NULL
76123
+ );
76124
+
76125
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_workspace_bindings_current
76126
+ ON workspace_bindings(is_current)
76127
+ WHERE is_current = 1;
76128
+
76129
+ CREATE INDEX IF NOT EXISTS idx_workspace_bindings_updated_at
76130
+ ON workspace_bindings(updated_at DESC);
76040
76131
  `;
76041
76132
  function absoluteDefaultStorePath() {
76042
76133
  return path12.join(homeDir(), ".agent-remnote", "store.sqlite");
@@ -76233,17 +76324,17 @@ function readAppliedMigrations(db) {
76233
76324
  return new Map;
76234
76325
  }
76235
76326
  }
76236
- function ensureMigrationRecorded(db, migration6, appliedAt, appVersion) {
76237
- const existing = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration6.version);
76327
+ function ensureMigrationRecorded(db, migration7, appliedAt, appVersion) {
76328
+ const existing = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration7.version);
76238
76329
  if (existing) {
76239
76330
  const actualChecksum = typeof existing?.checksum === "string" ? existing.checksum : String(existing?.checksum ?? "");
76240
- if (actualChecksum !== migration6.checksum) {
76331
+ if (actualChecksum !== migration7.checksum) {
76241
76332
  throw new StoreSchemaError({
76242
76333
  code: "STORE_SCHEMA_INVALID",
76243
76334
  message: "Store database migration drift detected",
76244
76335
  details: {
76245
- version: migration6.version,
76246
- expected: { name: migration6.name, checksum: migration6.checksum },
76336
+ version: migration7.version,
76337
+ expected: { name: migration7.name, checksum: migration7.checksum },
76247
76338
  actual: { name: String(existing?.name ?? ""), checksum: actualChecksum }
76248
76339
  },
76249
76340
  nextActions: ["Upgrade `agent-remnote` to a newer version", "agent-remnote doctor"]
@@ -76254,9 +76345,9 @@ function ensureMigrationRecorded(db, migration6, appliedAt, appVersion) {
76254
76345
  try {
76255
76346
  db.prepare(`INSERT INTO store_migrations(version, name, checksum, applied_at, app_version)
76256
76347
  VALUES(@version, @name, @checksum, @applied_at, @app_version)`).run({
76257
- version: migration6.version,
76258
- name: migration6.name,
76259
- checksum: migration6.checksum,
76348
+ version: migration7.version,
76349
+ name: migration7.name,
76350
+ checksum: migration7.checksum,
76260
76351
  applied_at: appliedAt,
76261
76352
  app_version: appVersion
76262
76353
  });
@@ -76264,15 +76355,15 @@ function ensureMigrationRecorded(db, migration6, appliedAt, appVersion) {
76264
76355
  const code2 = String(e?.code || "");
76265
76356
  if (code2 !== "SQLITE_CONSTRAINT" && code2 !== "SQLITE_CONSTRAINT_PRIMARYKEY" && code2 !== "SQLITE_CONSTRAINT_UNIQUE")
76266
76357
  throw e;
76267
- const again = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration6.version);
76358
+ const again = db.prepare(`SELECT name, checksum FROM store_migrations WHERE version=?`).get(migration7.version);
76268
76359
  const actualChecksum = typeof again?.checksum === "string" ? again.checksum : String(again?.checksum ?? "");
76269
- if (actualChecksum !== migration6.checksum) {
76360
+ if (actualChecksum !== migration7.checksum) {
76270
76361
  throw new StoreSchemaError({
76271
76362
  code: "STORE_SCHEMA_INVALID",
76272
76363
  message: "Store database migration drift detected",
76273
76364
  details: {
76274
- version: migration6.version,
76275
- expected: { name: migration6.name, checksum: migration6.checksum },
76365
+ version: migration7.version,
76366
+ expected: { name: migration7.name, checksum: migration7.checksum },
76276
76367
  actual: { name: String(again?.name ?? ""), checksum: actualChecksum }
76277
76368
  },
76278
76369
  nextActions: ["Upgrade `agent-remnote` to a newer version", "agent-remnote doctor"]
@@ -76307,8 +76398,8 @@ function ensureAuditUpTo(db, version) {
76307
76398
  }
76308
76399
  }
76309
76400
  function applyMigration(db, targetVersion) {
76310
- const migration6 = MIGRATION_PLAN.migrations[targetVersion - 1];
76311
- if (!migration6 || migration6.version !== targetVersion) {
76401
+ const migration7 = MIGRATION_PLAN.migrations[targetVersion - 1];
76402
+ if (!migration7 || migration7.version !== targetVersion) {
76312
76403
  throw new StoreSchemaError({
76313
76404
  code: "STORE_SCHEMA_UNKNOWN",
76314
76405
  message: `Unknown store migration target: ${targetVersion}`,
@@ -76316,8 +76407,8 @@ function applyMigration(db, targetVersion) {
76316
76407
  nextActions: ["Report this as a bug"]
76317
76408
  });
76318
76409
  }
76319
- migration6.apply(db);
76320
- ensureMigrationRecorded(db, migration6, Date.now(), appVersionForAudit());
76410
+ migration7.apply(db);
76411
+ ensureMigrationRecorded(db, migration7, Date.now(), appVersionForAudit());
76321
76412
  }
76322
76413
  function migrate(db, resolvedPath) {
76323
76414
  const initial = readUserVersion(db);
@@ -87251,7 +87342,7 @@ function loadCellsForRows(db, rowIds, ctx) {
87251
87342
  json_extract(doc, '$.parent') AS parentId
87252
87343
  FROM quanta
87253
87344
  WHERE json_extract(doc, '$.parent') IN (${placeholders})
87254
- AND json_extract(doc, '$.type') = 2`).all(...rowIds);
87345
+ `).all(...rowIds);
87255
87346
  for (const row of rows) {
87256
87347
  const doc = safeJsonParse(row.doc) ?? {};
87257
87348
  const parentId = typeof row.parentId === "string" ? row.parentId : "";
@@ -87263,6 +87354,8 @@ function loadCellsForRows(db, rowIds, ctx) {
87263
87354
  if (!propertyId)
87264
87355
  continue;
87265
87356
  const prop = ctx.propertiesById.get(propertyId);
87357
+ if (!prop)
87358
+ continue;
87266
87359
  const kind = String(prop?.kind ?? "unknown");
87267
87360
  const propertyName = String(prop?.name ?? propertyId);
87268
87361
  const valueTokens = doc.value;
@@ -87314,33 +87407,38 @@ function loadCellsForRows(db, rowIds, ctx) {
87314
87407
  return { cellsByRowId, dailyInfoById };
87315
87408
  }
87316
87409
  function loadProperties(db, tagId) {
87410
+ const propertyMarkerIds = loadPropertyMarkerIds(db);
87317
87411
  const stmt = db.prepare(`SELECT _id AS id,
87318
87412
  doc,
87319
87413
  json_extract(doc, '$.rcrs') AS rawType
87320
87414
  FROM quanta
87321
87415
  WHERE json_extract(doc, '$.parent') = @tagId
87322
- AND json_extract(doc, '$.rcrs') IS NOT NULL
87323
87416
  ORDER BY json_extract(doc, '$.f')`);
87324
87417
  const optionStmt = db.prepare(`SELECT _id AS id,
87325
87418
  doc,
87326
87419
  json_extract(doc, '$.rcre') AS rawOptionType
87327
87420
  FROM quanta
87328
87421
  WHERE json_extract(doc, '$.parent') = @parent
87329
- AND json_extract(doc, '$.rcre') IS NOT NULL
87330
87422
  ORDER BY json_extract(doc, '$.f')`);
87331
87423
  const properties = [];
87332
87424
  const optionNameById = new Map;
87333
87425
  const propertyRows = stmt.all({ tagId });
87334
87426
  for (const row of propertyRows) {
87335
87427
  const doc = safeJsonParse(row.doc);
87336
- const rawType = typeof row.rawType === "string" ? row.rawType : null;
87337
- const typeCode = rawType ? rawType.split(".")[1] ?? null : null;
87338
- const kind = mapPropertyType(typeCode);
87428
+ if (!isPropertyDoc(doc, propertyMarkerIds) && typeof row.rawType !== "string") {
87429
+ continue;
87430
+ }
87431
+ const fieldType = typeof doc?.ft === "string" ? String(doc.ft) : null;
87432
+ const rawType = typeof row.rawType === "string" ? row.rawType : fieldType;
87433
+ const typeCode = rawType ? rawType.split(".")[1] ?? rawType : null;
87339
87434
  const summary6 = summarizeKey(doc?.key, db, { expand: false, maxDepth: 0 });
87435
+ const propertyKindHint = typeCode ? mapPropertyType(typeCode) : null;
87340
87436
  const options6 = [];
87341
87437
  const optionRows = optionStmt.all({ parent: row.id });
87342
87438
  for (const optionRow of optionRows) {
87343
87439
  const optionDoc = safeJsonParse(optionRow.doc);
87440
+ if (!isOptionDoc(optionDoc, optionRow.rawOptionType, propertyMarkerIds, propertyKindHint))
87441
+ continue;
87344
87442
  const optionSummary = summarizeKey(optionDoc?.key, db, { expand: false, maxDepth: 0 });
87345
87443
  const pdRaw = optionDoc?.pd;
87346
87444
  const pdObject = typeof pdRaw === "string" ? safeJsonParse(pdRaw) : pdRaw;
@@ -87359,6 +87457,7 @@ function loadProperties(db, tagId) {
87359
87457
  });
87360
87458
  optionNameById.set(optionRow.id, optionSummary.text || optionRow.id);
87361
87459
  }
87460
+ const kind = inferPropertyKind({ typeCode, options: options6 });
87362
87461
  properties.push({
87363
87462
  id: row.id,
87364
87463
  name: summary6.text || row.id,
@@ -87372,6 +87471,50 @@ function loadProperties(db, tagId) {
87372
87471
  optionNameById
87373
87472
  };
87374
87473
  }
87474
+ function loadPropertyMarkerIds(db) {
87475
+ const rows = db.prepare(`SELECT _id AS id
87476
+ FROM quanta
87477
+ WHERE json_extract(doc, '$.rcrt') = 'y'`).all();
87478
+ return new Set(rows.map((row) => String(row.id ?? "")).filter(Boolean));
87479
+ }
87480
+ function isPropertyDoc(doc, propertyMarkerIds) {
87481
+ if (!doc || typeof doc !== "object")
87482
+ return false;
87483
+ const tp = doc.tp;
87484
+ if (!tp || typeof tp !== "object" || Array.isArray(tp))
87485
+ return false;
87486
+ for (const markerId of propertyMarkerIds) {
87487
+ const value8 = tp[markerId];
87488
+ if (!value8 || typeof value8 !== "object")
87489
+ continue;
87490
+ if (value8.t === true)
87491
+ return true;
87492
+ }
87493
+ return false;
87494
+ }
87495
+ function isOptionDoc(doc, rawOptionType, propertyMarkerIds, propertyKindHint) {
87496
+ if (!doc || typeof doc !== "object")
87497
+ return false;
87498
+ if (isPropertyDoc(doc, propertyMarkerIds))
87499
+ return false;
87500
+ if (doc.tc === true)
87501
+ return false;
87502
+ if (Array.isArray(doc.value))
87503
+ return false;
87504
+ const key = doc.key;
87505
+ if (!Array.isArray(key) || key.length === 0)
87506
+ return false;
87507
+ if (typeof rawOptionType === "string" && rawOptionType.trim())
87508
+ return true;
87509
+ return propertyKindHint === "select" || propertyKindHint === "multi_select";
87510
+ }
87511
+ function inferPropertyKind(params3) {
87512
+ if (params3.typeCode)
87513
+ return mapPropertyType(params3.typeCode);
87514
+ if (params3.options.length > 0)
87515
+ return "unknown";
87516
+ return "text";
87517
+ }
87375
87518
  function loadRows(db, tagId, options6) {
87376
87519
  const countRow = db.prepare(`SELECT COUNT(*) AS total
87377
87520
  FROM (
@@ -87401,16 +87544,22 @@ function mapPropertyType(code2) {
87401
87544
  return "unknown";
87402
87545
  switch (code2) {
87403
87546
  case "s":
87547
+ case "single_select":
87404
87548
  return "select";
87405
87549
  case "m":
87550
+ case "multi_select":
87406
87551
  return "multi_select";
87407
87552
  case "t":
87553
+ case "text":
87408
87554
  return "text";
87409
87555
  case "n":
87556
+ case "number":
87410
87557
  return "number";
87411
87558
  case "d":
87559
+ case "date":
87412
87560
  return "date";
87413
87561
  case "c":
87562
+ case "checkbox":
87414
87563
  return "checkbox";
87415
87564
  default:
87416
87565
  return `unknown(${code2})`;
@@ -92880,10 +93029,50 @@ var dailySummaryCommand = exports_Command.make("summary", { days: days2, maxLine
92880
93029
  yield* writeSuccess({ data: result, md: result.markdown ?? "" });
92881
93030
  }).pipe(catchAll2(writeFailure)));
92882
93031
 
92883
- // src/services/HostApiClient.ts
93032
+ // src/lib/apiUrls.ts
93033
+ function normalizeApiBasePath2(basePath) {
93034
+ const trimmed2 = basePath.trim();
93035
+ if (!trimmed2)
93036
+ return "/v1";
93037
+ const normalized = trimmed2.startsWith("/") ? trimmed2 : `/${trimmed2}`;
93038
+ const withoutTrailing = normalized.replace(/\/+$/, "");
93039
+ return withoutTrailing && withoutTrailing !== "/" ? withoutTrailing : "/";
93040
+ }
92884
93041
  function normalizeBaseUrl(baseUrl) {
92885
93042
  return baseUrl.trim().replace(/\/+$/, "");
92886
93043
  }
93044
+ function resolveBasePrefix(baseUrl, fallbackBasePath) {
93045
+ const parsed = new URL(normalizeBaseUrl(baseUrl));
93046
+ const pathname = normalizeApiBasePath2(parsed.pathname);
93047
+ if (pathname && pathname !== "/")
93048
+ return normalizeApiBasePath2(pathname);
93049
+ return normalizeApiBasePath2(fallbackBasePath);
93050
+ }
93051
+ function normalizeRoutePath(routePath) {
93052
+ const trimmed2 = routePath.trim();
93053
+ if (!trimmed2)
93054
+ return "";
93055
+ return `/${trimmed2.replace(/^\/+/, "")}`;
93056
+ }
93057
+ function buildApiBaseUrl(baseUrl, fallbackBasePath = "/v1") {
93058
+ const parsed = new URL(normalizeBaseUrl(baseUrl));
93059
+ const prefix2 = resolveBasePrefix(baseUrl, fallbackBasePath);
93060
+ return prefix2 === "/" ? parsed.origin : `${parsed.origin}${prefix2}`;
93061
+ }
93062
+ function apiLocalBaseUrl(port3, basePath = "/v1") {
93063
+ return buildApiBaseUrl(`http://127.0.0.1:${port3}`, basePath);
93064
+ }
93065
+ function apiContainerBaseUrl(port3, basePath = "/v1") {
93066
+ return buildApiBaseUrl(`http://host.docker.internal:${port3}`, basePath);
93067
+ }
93068
+ function joinApiUrl(baseUrl, routePath, fallbackBasePath = "/v1") {
93069
+ return `${buildApiBaseUrl(baseUrl, fallbackBasePath)}${normalizeRoutePath(routePath)}`;
93070
+ }
93071
+
93072
+ // src/services/HostApiClient.ts
93073
+ function normalizeBaseUrl2(baseUrl) {
93074
+ return baseUrl.trim().replace(/\/+$/, "");
93075
+ }
92887
93076
  function apiTimeoutError(params3) {
92888
93077
  return new CliError({
92889
93078
  code: "API_TIMEOUT",
@@ -92929,8 +93118,8 @@ function buildQuery(params3) {
92929
93118
  }
92930
93119
  function requestJson(params3) {
92931
93120
  const timeoutMs = Math.max(1, params3.timeoutMs ?? 15000);
92932
- const baseUrl = normalizeBaseUrl(params3.baseUrl);
92933
- const url2 = `${baseUrl}${params3.path}`;
93121
+ const baseUrl = normalizeBaseUrl2(params3.baseUrl);
93122
+ const url2 = joinApiUrl(baseUrl, params3.path, params3.basePath);
92934
93123
  return async((resume2) => {
92935
93124
  const controller = new AbortController;
92936
93125
  const timer2 = setTimeout(() => controller.abort(), timeoutMs);
@@ -92984,289 +93173,304 @@ function requestJson(params3) {
92984
93173
 
92985
93174
  class HostApiClient extends Tag2("HostApiClient")() {
92986
93175
  }
92987
- var HostApiClientLive = succeed10(HostApiClient, {
92988
- health: ({ baseUrl, timeoutMs }) => requestJson({ baseUrl, path: "/v1/health", method: "GET", timeoutMs }),
92989
- status: ({ baseUrl, timeoutMs }) => requestJson({ baseUrl, path: "/v1/status", method: "GET", timeoutMs }),
92990
- uiContextSnapshot: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => requestJson({
92991
- baseUrl,
92992
- path: `/v1/plugin/ui-context/snapshot${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
92993
- method: "GET",
92994
- timeoutMs
92995
- }),
92996
- uiContextPage: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => requestJson({
92997
- baseUrl,
92998
- path: `/v1/plugin/ui-context/page${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
92999
- method: "GET",
93000
- timeoutMs
93001
- }),
93002
- uiContextFocusedRem: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => requestJson({
93003
- baseUrl,
93004
- path: `/v1/plugin/ui-context/focused-rem${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93005
- method: "GET",
93006
- timeoutMs
93007
- }),
93008
- uiContextDescribe: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, selectionLimit, timeoutMs }) => requestJson({
93009
- baseUrl,
93010
- path: `/v1/plugin/ui-context/describe${buildQuery({ stateFile: stateFile3, staleMs: staleMs2, selectionLimit })}`,
93011
- method: "GET",
93012
- timeoutMs
93013
- }),
93014
- selectionSnapshot: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => requestJson({
93015
- baseUrl,
93016
- path: `/v1/plugin/selection/snapshot${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93017
- method: "GET",
93018
- timeoutMs
93019
- }),
93020
- selectionRoots: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => requestJson({
93021
- baseUrl,
93022
- path: `/v1/plugin/selection/roots${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93023
- method: "GET",
93024
- timeoutMs
93025
- }),
93026
- selectionCurrent: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => requestJson({
93027
- baseUrl,
93028
- path: `/v1/plugin/selection/current${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93029
- method: "GET",
93030
- timeoutMs
93031
- }),
93032
- pluginCurrent: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, selectionLimit, timeoutMs }) => requestJson({
93033
- baseUrl,
93034
- path: `/v1/plugin/current${buildQuery({ stateFile: stateFile3, staleMs: staleMs2, selectionLimit })}`,
93035
- method: "GET",
93036
- timeoutMs
93037
- }),
93038
- selectionOutline: ({ baseUrl, body, timeoutMs }) => requestJson({ baseUrl, path: "/v1/plugin/selection/outline", method: "POST", body, timeoutMs }),
93039
- uiContext: ({ baseUrl, timeoutMs }) => requestJson({ baseUrl, path: "/v1/ui-context", method: "GET", timeoutMs }),
93040
- selection: ({ baseUrl, timeoutMs }) => requestJson({ baseUrl, path: "/v1/selection", method: "GET", timeoutMs }),
93041
- searchDb: ({ baseUrl, ...body }) => requestJson({ baseUrl, path: "/v1/search/db", method: "POST", body }),
93042
- searchPlugin: ({ baseUrl, ...body }) => requestJson({ baseUrl, path: "/v1/search/plugin", method: "POST", body }),
93043
- writeApply: ({ baseUrl, body, timeoutMs }) => requestJson({ baseUrl, path: "/v1/write/apply", method: "POST", body, timeoutMs }),
93044
- readOutline: ({ baseUrl, body, timeoutMs }) => requestJson({ baseUrl, path: "/v1/read/outline", method: "POST", body, timeoutMs }),
93045
- dailyRemId: ({ baseUrl, date: date6, offsetDays, timeoutMs }) => requestJson({
93046
- baseUrl,
93047
- path: `/v1/daily/rem-id${buildQuery({ date: date6, offsetDays })}`,
93048
- method: "GET",
93049
- timeoutMs
93050
- }),
93051
- queueWait: ({ baseUrl, txnId, timeoutMs, pollMs }) => requestJson({ baseUrl, path: "/v1/queue/wait", method: "POST", body: { txnId, timeoutMs, pollMs } }),
93052
- queueTxn: ({ baseUrl, txnId, timeoutMs }) => requestJson({ baseUrl, path: `/v1/queue/txns/${encodeURIComponent(txnId)}`, method: "GET", timeoutMs }),
93053
- triggerSync: ({ baseUrl, timeoutMs }) => requestJson({ baseUrl, path: "/v1/actions/trigger-sync", method: "POST", timeoutMs })
93054
- });
93176
+ var HostApiClientLive = effect(HostApiClient, gen2(function* () {
93177
+ const cfg = yield* AppConfig;
93178
+ const basePath = cfg.apiBasePath ?? "/v1";
93179
+ const request = (params3) => requestJson({ ...params3, basePath });
93180
+ return {
93181
+ health: ({ baseUrl, timeoutMs }) => request({ baseUrl, path: "/health", method: "GET", timeoutMs }),
93182
+ status: ({ baseUrl, timeoutMs }) => request({ baseUrl, path: "/status", method: "GET", timeoutMs }),
93183
+ uiContextSnapshot: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => request({
93184
+ baseUrl,
93185
+ path: `/plugin/ui-context/snapshot${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93186
+ method: "GET",
93187
+ timeoutMs
93188
+ }),
93189
+ uiContextPage: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => request({
93190
+ baseUrl,
93191
+ path: `/plugin/ui-context/page${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93192
+ method: "GET",
93193
+ timeoutMs
93194
+ }),
93195
+ uiContextFocusedRem: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => request({
93196
+ baseUrl,
93197
+ path: `/plugin/ui-context/focused-rem${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93198
+ method: "GET",
93199
+ timeoutMs
93200
+ }),
93201
+ uiContextDescribe: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, selectionLimit, timeoutMs }) => request({
93202
+ baseUrl,
93203
+ path: `/plugin/ui-context/describe${buildQuery({ stateFile: stateFile3, staleMs: staleMs2, selectionLimit })}`,
93204
+ method: "GET",
93205
+ timeoutMs
93206
+ }),
93207
+ selectionSnapshot: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => request({
93208
+ baseUrl,
93209
+ path: `/plugin/selection/snapshot${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93210
+ method: "GET",
93211
+ timeoutMs
93212
+ }),
93213
+ selectionRoots: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => request({
93214
+ baseUrl,
93215
+ path: `/plugin/selection/roots${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93216
+ method: "GET",
93217
+ timeoutMs
93218
+ }),
93219
+ selectionCurrent: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, timeoutMs }) => request({
93220
+ baseUrl,
93221
+ path: `/plugin/selection/current${buildQuery({ stateFile: stateFile3, staleMs: staleMs2 })}`,
93222
+ method: "GET",
93223
+ timeoutMs
93224
+ }),
93225
+ pluginCurrent: ({ baseUrl, stateFile: stateFile3, staleMs: staleMs2, selectionLimit, timeoutMs }) => request({
93226
+ baseUrl,
93227
+ path: `/plugin/current${buildQuery({ stateFile: stateFile3, staleMs: staleMs2, selectionLimit })}`,
93228
+ method: "GET",
93229
+ timeoutMs
93230
+ }),
93231
+ selectionOutline: ({ baseUrl, body, timeoutMs }) => request({ baseUrl, path: "/plugin/selection/outline", method: "POST", body, timeoutMs }),
93232
+ uiContext: ({ baseUrl, timeoutMs }) => request({ baseUrl, path: "/ui-context", method: "GET", timeoutMs }),
93233
+ selection: ({ baseUrl, timeoutMs }) => request({ baseUrl, path: "/selection", method: "GET", timeoutMs }),
93234
+ searchDb: ({ baseUrl, ...body }) => request({ baseUrl, path: "/search/db", method: "POST", body }),
93235
+ searchPlugin: ({ baseUrl, ...body }) => request({ baseUrl, path: "/search/plugin", method: "POST", body }),
93236
+ writeApply: ({ baseUrl, body, timeoutMs }) => request({ baseUrl, path: "/write/apply", method: "POST", body, timeoutMs }),
93237
+ readOutline: ({ baseUrl, body, timeoutMs }) => request({ baseUrl, path: "/read/outline", method: "POST", body, timeoutMs }),
93238
+ dailyRemId: ({ baseUrl, date: date6, offsetDays, timeoutMs }) => request({
93239
+ baseUrl,
93240
+ path: `/daily/rem-id${buildQuery({ date: date6, offsetDays })}`,
93241
+ method: "GET",
93242
+ timeoutMs
93243
+ }),
93244
+ queueWait: ({ baseUrl, txnId, timeoutMs, pollMs }) => request({ baseUrl, path: "/queue/wait", method: "POST", body: { txnId, timeoutMs, pollMs } }),
93245
+ queueTxn: ({ baseUrl, txnId, timeoutMs }) => request({ baseUrl, path: `/queue/txns/${encodeURIComponent(txnId)}`, method: "GET", timeoutMs }),
93246
+ triggerSync: ({ baseUrl, timeoutMs }) => request({ baseUrl, path: "/actions/trigger-sync", method: "POST", timeoutMs })
93247
+ };
93248
+ }));
93055
93249
 
93056
- // src/services/RefResolver.ts
93057
- class RefResolver extends Tag2("RefResolver")() {
93250
+ // src/commands/read/selection/_shared.ts
93251
+ function normalizeId(raw4) {
93252
+ return typeof raw4 === "string" ? raw4.trim() : "";
93058
93253
  }
93059
- function stripQuotes(s) {
93060
- const t = s.trim();
93061
- if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
93062
- return t.slice(1, -1);
93254
+ function parseSelectionInfo(raw4) {
93255
+ const kindRaw = typeof raw4?.kind === "string" ? raw4.kind.trim() : "";
93256
+ const selectionType = typeof raw4?.selectionType === "string" ? raw4.selectionType : undefined;
93257
+ const updatedAtRaw = Number(raw4?.updatedAt ?? 0);
93258
+ const updatedAt = Number.isFinite(updatedAtRaw) && updatedAtRaw > 0 ? updatedAtRaw : 0;
93259
+ if (kindRaw === "text" || kindRaw === "" && selectionType === "Text") {
93260
+ const remId = normalizeId(raw4?.remId);
93261
+ const startRaw = raw4?.range?.start;
93262
+ const endRaw = raw4?.range?.end;
93263
+ const start4 = typeof startRaw === "number" && Number.isFinite(startRaw) ? Math.floor(startRaw) : NaN;
93264
+ const end6 = typeof endRaw === "number" && Number.isFinite(endRaw) ? Math.floor(endRaw) : NaN;
93265
+ const isReverse = raw4?.isReverse === true;
93266
+ if (remId && Number.isFinite(start4) && Number.isFinite(end6) && start4 !== end6) {
93267
+ return { kind: "text", selectionType, remId, range: { start: start4, end: end6 }, isReverse, updatedAt };
93268
+ }
93269
+ return { kind: "none", selectionType: undefined, updatedAt };
93063
93270
  }
93064
- return t;
93271
+ if (kindRaw === "rem" || kindRaw === "" || selectionType === "Rem") {
93272
+ const totalCountRaw = Number(raw4?.totalCount ?? 0);
93273
+ const remIds = Array.isArray(raw4?.remIds) ? raw4.remIds.map(normalizeId).filter(Boolean) : [];
93274
+ const totalCount = Number.isFinite(totalCountRaw) && totalCountRaw >= 0 ? Math.floor(totalCountRaw) : remIds.length;
93275
+ const truncated = !!raw4?.truncated || totalCount > remIds.length;
93276
+ if (remIds.length > 0 || totalCount > 0) {
93277
+ return { kind: "rem", selectionType, totalCount, truncated, remIds, updatedAt };
93278
+ }
93279
+ }
93280
+ return { kind: "none", selectionType: undefined, updatedAt };
93065
93281
  }
93066
- function parseRef(input) {
93067
- const raw4 = input.trim();
93068
- const link3 = tryParseRemnoteLink(raw4);
93069
- if (link3?.remId) {
93070
- return { kind: "id", value: link3.remId };
93282
+ function loadBridgeSelectionSnapshot(params3) {
93283
+ const resolved = resolveStateFilePath2(params3.stateFile);
93284
+ const stateFilePath = resolved.path;
93285
+ const now2 = Date.now();
93286
+ const staleThreshold = resolveStaleMs2(params3.staleMs);
93287
+ if (resolved.disabled) {
93288
+ return {
93289
+ status: "off",
93290
+ state_file: stateFilePath,
93291
+ updatedAt: 0,
93292
+ now: now2,
93293
+ stale_ms: staleThreshold,
93294
+ clients: 0
93295
+ };
93071
93296
  }
93072
- const idx = raw4.indexOf(":");
93073
- if (idx <= 0) {
93074
- throw new CliError({
93075
- code: "INVALID_ARGS",
93076
- message: `Invalid ref: ${input}`,
93077
- exitCode: 2,
93078
- hint: [
93079
- "Example: --ref id:xxx",
93080
- 'Example: --ref "remnote://w/<workspaceId>/<remId>"',
93081
- "Example: --ref page:Demo",
93082
- "Example: --ref title:Demo",
93083
- "Example: --ref daily:today",
93084
- "Example: --ref daily:-1"
93085
- ]
93086
- });
93297
+ const state = readJson2(stateFilePath);
93298
+ if (!state) {
93299
+ return {
93300
+ status: "down",
93301
+ state_file: stateFilePath,
93302
+ updatedAt: 0,
93303
+ now: now2,
93304
+ stale_ms: staleThreshold,
93305
+ clients: 0
93306
+ };
93087
93307
  }
93088
- const kind = raw4.slice(0, idx).trim();
93089
- const value8 = stripQuotes(raw4.slice(idx + 1));
93090
- if (!value8) {
93091
- throw new CliError({
93092
- code: "INVALID_ARGS",
93093
- message: `Invalid ref (missing value): ${input}`,
93094
- exitCode: 2
93095
- });
93308
+ const updatedAt = Number(state.updatedAt ?? 0);
93309
+ const isStale = !Number.isFinite(updatedAt) || updatedAt <= 0 || now2 - updatedAt > staleThreshold;
93310
+ const clients = Array.isArray(state.clients) ? state.clients : [];
93311
+ const activeConnIdRaw = typeof state.activeWorkerConnId === "string" ? state.activeWorkerConnId.trim() : "";
93312
+ const client = pickClient2(clients, params3.connId || activeConnIdRaw || undefined);
93313
+ if (!client) {
93314
+ return {
93315
+ status: "no_client",
93316
+ state_file: stateFilePath,
93317
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
93318
+ now: now2,
93319
+ stale_ms: staleThreshold,
93320
+ clients: clients.length
93321
+ };
93096
93322
  }
93097
- if (kind === "id" || kind === "title" || kind === "daily") {
93098
- if (kind === "id") {
93099
- const link22 = tryParseRemnoteLink(value8);
93100
- return { kind, value: link22?.remId ?? value8 };
93101
- }
93102
- return { kind, value: value8 };
93323
+ const selection = parseSelectionInfo(client.selection);
93324
+ if (isStale) {
93325
+ return {
93326
+ status: "stale",
93327
+ state_file: stateFilePath,
93328
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
93329
+ now: now2,
93330
+ stale_ms: staleThreshold,
93331
+ clients: clients.length,
93332
+ selection
93333
+ };
93103
93334
  }
93104
- if (kind === "page") {
93105
- return { kind, value: value8 };
93335
+ return {
93336
+ status: "ok",
93337
+ state_file: stateFilePath,
93338
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
93339
+ now: now2,
93340
+ stale_ms: staleThreshold,
93341
+ clients: clients.length,
93342
+ selection
93343
+ };
93344
+ }
93345
+ function requireOkSelection(snapshot2) {
93346
+ if (snapshot2.status === "ok" && snapshot2.selection) {
93347
+ return succeed8(snapshot2.selection);
93106
93348
  }
93107
- throw new CliError({
93108
- code: "INVALID_ARGS",
93109
- message: `Unsupported ref: ${input}`,
93110
- exitCode: 2,
93111
- hint: ["Supported prefixes: id:/page:/title:/daily:"]
93112
- });
93113
- }
93114
- function parseDailyOffset(value8) {
93115
- const v = value8.trim().toLowerCase();
93116
- if (v === "today" || v === "now" || v === "0")
93117
- return 0;
93118
- if (v === "yesterday")
93119
- return -1;
93120
- if (v === "tomorrow")
93121
- return 1;
93122
- const n = Number.parseInt(v, 10);
93123
- if (!Number.isFinite(n)) {
93124
- throw new CliError({
93125
- code: "INVALID_ARGS",
93126
- message: `Invalid daily ref: ${value8} (expected today/yesterday/tomorrow or an integer offset)`,
93127
- exitCode: 2
93128
- });
93129
- }
93130
- return n;
93349
+ const msg = snapshot2.status === "off" ? "WS state is disabled (REMNOTE_WS_STATE_FILE=0)" : snapshot2.status === "down" ? "WS state file not found: the daemon may not be running or has not written the state file yet" : snapshot2.status === "stale" ? "WS state is stale: the daemon may have stopped or has not updated for a long time" : snapshot2.status === "no_client" ? "No RemNote client is currently connected to the daemon" : "Selection is unavailable";
93350
+ return fail8(new CliError({
93351
+ code: "WS_UNAVAILABLE",
93352
+ message: msg,
93353
+ exitCode: 1,
93354
+ details: snapshot2
93355
+ }));
93131
93356
  }
93132
- var RefResolverLive = succeed10(RefResolver, {
93133
- resolve: (ref) => gen2(function* () {
93134
- const cfg = yield* AppConfig;
93135
- const parsed = yield* try_3({
93136
- try: () => parseRef(ref),
93137
- catch: (e) => e && typeof e === "object" && e._tag === "CliError" ? e : new CliError({ code: "INVALID_ARGS", message: `Invalid ref: ${ref}`, exitCode: 2 })
93138
- });
93139
- if (cfg.apiBaseUrl && parsed.kind !== "id") {
93140
- return yield* fail8(remoteModeUnsupportedError({
93141
- command: `ref resolution (${ref})`,
93142
- reason: "this path still resolves refs by reading the local RemNote database",
93143
- hints: [
93144
- "Use a remote-capable command that accepts --ref and forwards it to the host API.",
93145
- "If no remote endpoint exists yet, run the command on the host."
93146
- ],
93147
- apiBaseUrl: cfg.apiBaseUrl
93148
- }));
93149
- }
93150
- if (parsed.kind === "id")
93151
- return parsed.value;
93152
- const dailyOffset = parsed.kind === "daily" ? yield* try_3({
93153
- try: () => parseDailyOffset(parsed.value),
93154
- catch: (e) => e && typeof e === "object" && e._tag === "CliError" ? e : new CliError({
93155
- code: "INVALID_ARGS",
93156
- message: `Invalid daily ref: ${parsed.value}`,
93157
- exitCode: 2
93158
- })
93159
- }) : undefined;
93160
- const queryInput = parsed.kind === "title" || parsed.kind === "page" ? { query: parsed.value } : { query: "date", useCurrentDate: true, dateOffsetDays: dailyOffset };
93161
- const result = yield* tryPromise2({
93162
- try: async () => await executeSearchRemOverview({
93163
- ...queryInput,
93164
- dbPath: cfg.remnoteDb,
93165
- limit: 1,
93166
- preferExact: true,
93167
- exactFirstSingle: true,
93168
- pagesOnly: parsed.kind === "page" ? true : undefined
93169
- }),
93170
- catch: (e) => new CliError({
93171
- code: "DB_UNAVAILABLE",
93172
- message: String(e?.message || e || "RemNote DB is unavailable"),
93173
- exitCode: 1
93174
- })
93175
- });
93176
- const first3 = Array.isArray(result.matches) ? result.matches[0] : undefined;
93177
- const id2 = first3?.id ? String(first3.id) : "";
93178
- if (!id2) {
93179
- return yield* fail8(new CliError({
93357
+ function requireOkRemSelection(snapshot2) {
93358
+ return requireOkSelection(snapshot2).pipe(flatMap9((sel) => {
93359
+ if (sel.kind === "rem")
93360
+ return succeed8(sel);
93361
+ if (sel.kind === "text") {
93362
+ return fail8(new CliError({
93180
93363
  code: "INVALID_ARGS",
93181
- message: `No Rem found for ref: ${ref}`,
93182
- exitCode: 2
93364
+ message: "Current selection is text; this command requires Rem selection",
93365
+ exitCode: 2,
93366
+ details: snapshot2
93183
93367
  }));
93184
93368
  }
93185
- return id2;
93186
- })
93187
- });
93188
-
93189
- // src/commands/daily/rem-id.ts
93190
- function optionToUndefined10(opt) {
93191
- return isSome2(opt) ? opt.value : undefined;
93369
+ return fail8(new CliError({
93370
+ code: "INVALID_ARGS",
93371
+ message: "No Rem is currently selected",
93372
+ exitCode: 2,
93373
+ details: snapshot2
93374
+ }));
93375
+ }));
93192
93376
  }
93193
- function parseDateInput(raw4) {
93194
- const trimmed2 = raw4.trim();
93195
- const match24 = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed2);
93196
- const value8 = match24 ? new Date(Number(match24[1]), Number(match24[2]) - 1, Number(match24[3])) : new Date(trimmed2);
93197
- if (Number.isNaN(value8.getTime())) {
93198
- throw new CliError({ code: "INVALID_ARGS", message: `Invalid date: ${raw4}`, exitCode: 2 });
93199
- }
93200
- if (match24 && (value8.getFullYear() !== Number(match24[1]) || value8.getMonth() !== Number(match24[2]) - 1 || value8.getDate() !== Number(match24[3]))) {
93201
- throw new CliError({ code: "INVALID_ARGS", message: `Invalid date: ${raw4}`, exitCode: 2 });
93377
+
93378
+ // src/commands/read/uiContext/_shared.ts
93379
+ function loadBridgeUiContextSnapshot(params3) {
93380
+ const resolved = resolveStateFilePath2(params3.stateFile);
93381
+ const stateFilePath = resolved.path;
93382
+ const now2 = Date.now();
93383
+ const staleThreshold = resolveStaleMs2(params3.staleMs);
93384
+ if (resolved.disabled) {
93385
+ return {
93386
+ status: "off",
93387
+ state_file: stateFilePath,
93388
+ updatedAt: 0,
93389
+ now: now2,
93390
+ stale_ms: staleThreshold,
93391
+ clients: 0
93392
+ };
93202
93393
  }
93203
- return value8;
93204
- }
93205
- var date6 = text9("date").pipe(optional5, map34(optionToUndefined10));
93206
- var offsetDays = integer7("offset-days").pipe(optional5, map34(optionToUndefined10));
93207
- var dailyRemIdCommand = exports_Command.make("rem-id", { date: date6, offsetDays }, ({ date: date7, offsetDays: offsetDays2 }) => gen2(function* () {
93208
- if (date7 && offsetDays2 !== undefined) {
93209
- return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "Choose only one of --date or --offset-days", exitCode: 2 }));
93394
+ const state = readJson2(stateFilePath);
93395
+ if (!state) {
93396
+ return {
93397
+ status: "down",
93398
+ state_file: stateFilePath,
93399
+ updatedAt: 0,
93400
+ now: now2,
93401
+ stale_ms: staleThreshold,
93402
+ clients: 0
93403
+ };
93210
93404
  }
93211
- const cfg = yield* AppConfig;
93212
- const hostApi = yield* HostApiClient;
93213
- const refs = yield* RefResolver;
93214
- const remDb = yield* RemDb;
93215
- if (cfg.apiBaseUrl) {
93216
- const data = yield* hostApi.dailyRemId({
93217
- baseUrl: cfg.apiBaseUrl,
93218
- date: date7,
93219
- offsetDays: offsetDays2
93220
- });
93221
- yield* writeSuccess({
93222
- data,
93223
- ids: [data.remId],
93224
- md: `- ref: ${data.ref}
93225
- - rem_id: ${data.remId}${data.dateString ? `
93226
- - date_string: ${data.dateString}` : ""}
93227
- `
93228
- });
93229
- return;
93405
+ const updatedAt = Number(state.updatedAt ?? 0);
93406
+ const isStale = !Number.isFinite(updatedAt) || updatedAt <= 0 || now2 - updatedAt > staleThreshold;
93407
+ const clients = Array.isArray(state.clients) ? state.clients : [];
93408
+ const activeConnIdRaw = typeof state.activeWorkerConnId === "string" ? state.activeWorkerConnId.trim() : "";
93409
+ const client = pickClient2(clients, params3.connId || activeConnIdRaw || undefined);
93410
+ if (!client) {
93411
+ return {
93412
+ status: "no_client",
93413
+ state_file: stateFilePath,
93414
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
93415
+ now: now2,
93416
+ stale_ms: staleThreshold,
93417
+ clients: clients.length
93418
+ };
93230
93419
  }
93231
- let ref = `daily:${offsetDays2 ?? 0}`;
93232
- let remId = "";
93233
- let dateString;
93234
- if (date7) {
93235
- const target2 = parseDateInput(date7);
93236
- dateString = yield* remDb.withDb(cfg.remnoteDb, async (db) => {
93237
- const format9 = await getDateFormatting(db) ?? "yyyy/MM/dd";
93238
- return formatDateWithPattern(target2, format9);
93239
- }).pipe(map17((r) => r.result), catchAll2(() => succeed8(formatDateWithPattern(target2, "yyyy/MM/dd"))));
93240
- const searchInput = {
93241
- query: dateString,
93242
- dbPath: cfg.remnoteDb,
93243
- limit: 1,
93244
- preferExact: true,
93245
- exactFirstSingle: true,
93246
- excludePages: true
93420
+ const url2 = typeof client.uiContext?.url === "string" ? client.uiContext.url : "";
93421
+ const paneId = typeof client.uiContext?.paneId === "string" ? client.uiContext.paneId : "";
93422
+ const pageRemId = typeof client.uiContext?.pageRemId === "string" ? client.uiContext.pageRemId : "";
93423
+ const focusedRemId = typeof client.uiContext?.focusedRemId === "string" ? client.uiContext.focusedRemId : "";
93424
+ const focusedPortalId = typeof client.uiContext?.focusedPortalId === "string" ? client.uiContext.focusedPortalId : "";
93425
+ const kbId = typeof client.uiContext?.kbId === "string" ? client.uiContext.kbId : undefined;
93426
+ const kbName = typeof client.uiContext?.kbName === "string" ? client.uiContext.kbName : undefined;
93427
+ const source = typeof client.uiContext?.source === "string" ? client.uiContext.source : undefined;
93428
+ const ctxUpdatedAtRaw = Number(client.uiContext?.updatedAt ?? 0);
93429
+ const ctxUpdatedAt = Number.isFinite(ctxUpdatedAtRaw) && ctxUpdatedAtRaw > 0 ? ctxUpdatedAtRaw : 0;
93430
+ const uiContext = {
93431
+ url: url2,
93432
+ paneId,
93433
+ pageRemId,
93434
+ focusedRemId,
93435
+ focusedPortalId,
93436
+ kbId,
93437
+ kbName,
93438
+ source,
93439
+ updatedAt: ctxUpdatedAt
93440
+ };
93441
+ if (isStale) {
93442
+ return {
93443
+ status: "stale",
93444
+ state_file: stateFilePath,
93445
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
93446
+ now: now2,
93447
+ stale_ms: staleThreshold,
93448
+ clients: clients.length,
93449
+ ui_context: uiContext
93247
93450
  };
93248
- const result = yield* tryPromise2({
93249
- try: async () => await executeSearchRemOverview(searchInput),
93250
- catch: (e) => cliErrorFromUnknown(e, { code: "DB_UNAVAILABLE" })
93251
- });
93252
- const first3 = Array.isArray(result.matches) ? result.matches[0] : undefined;
93253
- remId = first3?.id ? String(first3.id) : "";
93254
- ref = `daily:${date7}`;
93255
- if (!remId) {
93256
- return yield* fail8(new CliError({ code: "INVALID_ARGS", message: `No Daily Rem found for date: ${date7}`, exitCode: 2 }));
93257
- }
93258
- } else {
93259
- remId = yield* refs.resolve(ref);
93260
93451
  }
93261
- yield* writeSuccess({
93262
- data: { ref, remId, dateString },
93263
- ids: [remId],
93264
- md: `- ref: ${ref}
93265
- - rem_id: ${remId}${dateString ? `
93266
- - date_string: ${dateString}` : ""}
93267
- `
93268
- });
93269
- }).pipe(catchAll2(writeFailure)));
93452
+ return {
93453
+ status: "ok",
93454
+ state_file: stateFilePath,
93455
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
93456
+ now: now2,
93457
+ stale_ms: staleThreshold,
93458
+ clients: clients.length,
93459
+ ui_context: uiContext
93460
+ };
93461
+ }
93462
+ function requireOkUiContext(snapshot2) {
93463
+ if (snapshot2.status === "ok" && snapshot2.ui_context) {
93464
+ return succeed8(snapshot2.ui_context);
93465
+ }
93466
+ const msg = snapshot2.status === "off" ? "WS state is disabled (REMNOTE_WS_STATE_FILE=0)" : snapshot2.status === "down" ? "WS state file not found: the daemon may not be running or has not written the state file yet" : snapshot2.status === "stale" ? "WS state is stale: the daemon may have stopped or has not updated for a long time" : snapshot2.status === "no_client" ? "No RemNote client is currently connected to the daemon" : "UI context is unavailable";
93467
+ return fail8(new CliError({
93468
+ code: "WS_UNAVAILABLE",
93469
+ message: msg,
93470
+ exitCode: 1,
93471
+ details: snapshot2
93472
+ }));
93473
+ }
93270
93474
  // src/kernel/write-plan/compile.ts
93271
93475
  var ALIAS_RE = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
93272
93476
  function getObject(value8) {
@@ -93694,23 +93898,80 @@ var PayloadLive = succeed10(Payload, {
93694
93898
  normalizeKeys
93695
93899
  });
93696
93900
 
93697
- // src/commands/_enqueue.ts
93698
- function normalizeOp(raw4, normalizer) {
93699
- if (!raw4 || typeof raw4 !== "object") {
93700
- throw new CliError({ code: "INVALID_PAYLOAD", message: "op must be an object", exitCode: 2 });
93701
- }
93702
- const type2 = typeof raw4.type === "string" ? raw4.type.trim() : "";
93703
- if (!type2) {
93704
- throw new CliError({ code: "INVALID_PAYLOAD", message: "op.type is required and must be a string", exitCode: 2 });
93705
- }
93706
- const canonicalType = canonicalizeOpType(type2);
93707
- if (!canonicalType) {
93708
- throw new CliError({ code: "INVALID_PAYLOAD", message: "op.type is required and must be a string", exitCode: 2 });
93709
- }
93710
- const payload = normalizer(raw4.payload ?? {});
93711
- const idempotencyKey = typeof raw4.idempotencyKey === "string" ? raw4.idempotencyKey : typeof raw4.idempotency_key === "string" ? raw4.idempotency_key : undefined;
93712
- const maxAttempts = typeof raw4.maxAttempts === "number" ? raw4.maxAttempts : typeof raw4.max_attempts === "number" ? raw4.max_attempts : undefined;
93713
- const deliverAfterMs = typeof raw4.deliverAfterMs === "number" ? raw4.deliverAfterMs : typeof raw4.deliver_after_ms === "number" ? raw4.deliver_after_ms : undefined;
93901
+ // src/commands/write/_propertyTypeRuntimeGuard.ts
93902
+ function propertyTypeCapabilityHint(scopeLabel) {
93903
+ return [
93904
+ `Create a plain ${scopeLabel} property without --type/--options if you only need the column shell.`,
93905
+ "Configure property type manually in the RemNote UI when you need select/number/date/checkbox behavior.",
93906
+ "If a property is already select-like, option mutation must run against the host/local RemNote DB rather than a caller-side remote mirror."
93907
+ ];
93908
+ }
93909
+ function typedPropertyCreationUnsupportedError(params3) {
93910
+ const hasType = typeof params3.type === "string" && params3.type.trim().length > 0;
93911
+ return new CliError({
93912
+ code: "WRITE_UNAVAILABLE",
93913
+ message: `Typed property creation is currently unsupported in the RemNote plugin runtime for ${params3.scopeLabel} properties`,
93914
+ exitCode: 1,
93915
+ details: {
93916
+ scope: params3.scopeLabel,
93917
+ requested_type: hasType ? params3.type?.trim() : undefined,
93918
+ requested_options: params3.hasOptions
93919
+ },
93920
+ hint: propertyTypeCapabilityHint(params3.scopeLabel)
93921
+ });
93922
+ }
93923
+ function propertyTypeMutationUnsupportedError(scopeLabel) {
93924
+ return new CliError({
93925
+ code: "WRITE_UNAVAILABLE",
93926
+ message: `Property type mutation is currently unsupported in the RemNote plugin runtime for ${scopeLabel} properties`,
93927
+ exitCode: 1,
93928
+ details: { scope: scopeLabel },
93929
+ hint: propertyTypeCapabilityHint(scopeLabel)
93930
+ });
93931
+ }
93932
+ function assertTypedPropertyCreationSupported(params3) {
93933
+ const hasType = typeof params3.type === "string" && params3.type.trim().length > 0;
93934
+ if (!hasType && !params3.hasOptions) {
93935
+ return;
93936
+ }
93937
+ throw typedPropertyCreationUnsupportedError(params3);
93938
+ }
93939
+ function ensureTypedPropertyCreationSupported(params3) {
93940
+ return try_3({
93941
+ try: () => assertTypedPropertyCreationSupported(params3),
93942
+ catch: (error4) => isCliError(error4) ? error4 : new CliError({
93943
+ code: "INTERNAL",
93944
+ message: "Failed to validate property type runtime capability",
93945
+ exitCode: 1,
93946
+ details: { error: String(error4?.message || error4) }
93947
+ })
93948
+ });
93949
+ }
93950
+ function assertSupportedPropertyTypeMutation(scopeLabel) {
93951
+ throw propertyTypeMutationUnsupportedError(scopeLabel);
93952
+ }
93953
+ function failUnsupportedPropertyTypeMutation(scopeLabel) {
93954
+ return fail8(propertyTypeMutationUnsupportedError(scopeLabel));
93955
+ }
93956
+
93957
+ // src/commands/_enqueue.ts
93958
+ function normalizeOp(raw4, normalizer) {
93959
+ if (!raw4 || typeof raw4 !== "object") {
93960
+ throw new CliError({ code: "INVALID_PAYLOAD", message: "op must be an object", exitCode: 2 });
93961
+ }
93962
+ const type2 = typeof raw4.type === "string" ? raw4.type.trim() : "";
93963
+ if (!type2) {
93964
+ throw new CliError({ code: "INVALID_PAYLOAD", message: "op.type is required and must be a string", exitCode: 2 });
93965
+ }
93966
+ const canonicalType = canonicalizeOpType(type2);
93967
+ if (!canonicalType) {
93968
+ throw new CliError({ code: "INVALID_PAYLOAD", message: "op.type is required and must be a string", exitCode: 2 });
93969
+ }
93970
+ const payload = normalizer(raw4.payload ?? {});
93971
+ assertPropertyTypeRuntimeSupported(canonicalType, payload);
93972
+ const idempotencyKey = typeof raw4.idempotencyKey === "string" ? raw4.idempotencyKey : typeof raw4.idempotency_key === "string" ? raw4.idempotency_key : undefined;
93973
+ const maxAttempts = typeof raw4.maxAttempts === "number" ? raw4.maxAttempts : typeof raw4.max_attempts === "number" ? raw4.max_attempts : undefined;
93974
+ const deliverAfterMs = typeof raw4.deliverAfterMs === "number" ? raw4.deliverAfterMs : typeof raw4.deliver_after_ms === "number" ? raw4.deliver_after_ms : undefined;
93714
93975
  return {
93715
93976
  type: canonicalType,
93716
93977
  payload,
@@ -93719,6 +93980,18 @@ function normalizeOp(raw4, normalizer) {
93719
93980
  deliverAfterMs
93720
93981
  };
93721
93982
  }
93983
+ function assertPropertyTypeRuntimeSupported(canonicalType, payload) {
93984
+ if (canonicalType === "set_property_type") {
93985
+ assertSupportedPropertyTypeMutation("generic");
93986
+ }
93987
+ if (canonicalType === "add_property") {
93988
+ assertTypedPropertyCreationSupported({
93989
+ scopeLabel: "generic",
93990
+ type: typeof payload?.type === "string" ? payload.type : undefined,
93991
+ hasOptions: Array.isArray(payload?.options)
93992
+ });
93993
+ }
93994
+ }
93722
93995
  var OP_TYPES_REQUIRE_PARENT_ID = new Set([
93723
93996
  "create_rem",
93724
93997
  "create_portal",
@@ -93868,8 +94141,535 @@ function normalizeOps(rawOps) {
93868
94141
  details: { error: String(e?.message || e) }
93869
94142
  })
93870
94143
  });
93871
- });
93872
- }
94144
+ });
94145
+ }
94146
+
94147
+ // src/services/RefResolver.ts
94148
+ import { homedir } from "node:os";
94149
+ import path24 from "node:path";
94150
+
94151
+ // src/lib/workspaceResolver.ts
94152
+ import fs18 from "node:fs";
94153
+
94154
+ // src/services/WorkspaceBindings.ts
94155
+ class WorkspaceBindings extends Tag2("WorkspaceBindings")() {
94156
+ }
94157
+ function normalizeText(value8, field) {
94158
+ const normalized = String(value8 ?? "").trim();
94159
+ if (!normalized) {
94160
+ throw new CliError({
94161
+ code: "INVALID_ARGS",
94162
+ message: `${field} must be a non-empty string`,
94163
+ exitCode: 2,
94164
+ details: { field, value: value8 }
94165
+ });
94166
+ }
94167
+ return normalized;
94168
+ }
94169
+ function normalizeOptionalText(value8) {
94170
+ const normalized = String(value8 ?? "").trim();
94171
+ return normalized ? normalized : undefined;
94172
+ }
94173
+ function normalizeTimestamp(value8, fallback) {
94174
+ if (value8 === undefined)
94175
+ return fallback;
94176
+ if (!Number.isFinite(value8) || value8 <= 0) {
94177
+ throw new CliError({
94178
+ code: "INVALID_ARGS",
94179
+ message: "timestamp must be a positive integer",
94180
+ exitCode: 2,
94181
+ details: { value: value8 }
94182
+ });
94183
+ }
94184
+ return Math.floor(value8);
94185
+ }
94186
+ function mapStoreError(storeDbPath, error4) {
94187
+ const code2 = error4.code === "STORE_SCHEMA_NEWER" ? "QUEUE_SCHEMA_NEWER" : error4.code === "STORE_SCHEMA_INVALID" ? "QUEUE_SCHEMA_INVALID" : "QUEUE_SCHEMA_UNKNOWN";
94188
+ return new CliError({
94189
+ code: code2,
94190
+ message: error4.message,
94191
+ exitCode: 1,
94192
+ details: { store_db: storeDbPath, ...error4.details || {} },
94193
+ hint: [...Array.isArray(error4.nextActions) ? error4.nextActions : [], "Override the store db path with --store-db"]
94194
+ });
94195
+ }
94196
+ function mapRow(row) {
94197
+ if (!row)
94198
+ return;
94199
+ return {
94200
+ workspaceId: String(row.workspace_id),
94201
+ kbName: row.kb_name ?? undefined,
94202
+ dbPath: String(row.db_path),
94203
+ source: row.source,
94204
+ isCurrent: Number(row.is_current) === 1,
94205
+ firstSeenAt: Number(row.first_seen_at),
94206
+ lastVerifiedAt: Number(row.last_verified_at),
94207
+ lastUiContextAt: row.last_ui_context_at === null ? undefined : Number(row.last_ui_context_at),
94208
+ updatedAt: Number(row.updated_at)
94209
+ };
94210
+ }
94211
+ function withStoreDb(storeDbPath, fn) {
94212
+ const db = openStoreDb(storeDbPath);
94213
+ try {
94214
+ return fn(db);
94215
+ } finally {
94216
+ db.close();
94217
+ }
94218
+ }
94219
+ var WorkspaceBindingsLive = succeed10(WorkspaceBindings, {
94220
+ getCurrent: ({ storeDbPath }) => try_3({
94221
+ try: () => withStoreDb(storeDbPath, (db) => mapRow(db.prepare(`SELECT workspace_id, kb_name, db_path, source, is_current, first_seen_at, last_verified_at, last_ui_context_at, updated_at
94222
+ FROM workspace_bindings
94223
+ WHERE is_current = 1
94224
+ LIMIT 1`).get())),
94225
+ catch: (error4) => {
94226
+ if (isCliError(error4))
94227
+ return error4;
94228
+ if (error4 instanceof StoreSchemaError)
94229
+ return mapStoreError(storeDbPath, error4);
94230
+ return new CliError({
94231
+ code: "QUEUE_UNAVAILABLE",
94232
+ message: "Store database is unavailable",
94233
+ exitCode: 1,
94234
+ details: { store_db: storeDbPath, error: String(error4?.message || error4) }
94235
+ });
94236
+ }
94237
+ }),
94238
+ getByWorkspaceId: ({ storeDbPath, workspaceId }) => try_3({
94239
+ try: () => withStoreDb(storeDbPath, (db) => mapRow(db.prepare(`SELECT workspace_id, kb_name, db_path, source, is_current, first_seen_at, last_verified_at, last_ui_context_at, updated_at
94240
+ FROM workspace_bindings
94241
+ WHERE workspace_id = ?
94242
+ LIMIT 1`).get(normalizeText(workspaceId, "workspaceId")))),
94243
+ catch: (error4) => {
94244
+ if (isCliError(error4))
94245
+ return error4;
94246
+ if (error4 instanceof StoreSchemaError)
94247
+ return mapStoreError(storeDbPath, error4);
94248
+ return new CliError({
94249
+ code: "QUEUE_UNAVAILABLE",
94250
+ message: "Store database is unavailable",
94251
+ exitCode: 1,
94252
+ details: { store_db: storeDbPath, error: String(error4?.message || error4) }
94253
+ });
94254
+ }
94255
+ }),
94256
+ list: ({ storeDbPath }) => try_3({
94257
+ try: () => withStoreDb(storeDbPath, (db) => {
94258
+ const rows = db.prepare(`SELECT workspace_id, kb_name, db_path, source, is_current, first_seen_at, last_verified_at, last_ui_context_at, updated_at
94259
+ FROM workspace_bindings
94260
+ ORDER BY is_current DESC, updated_at DESC, workspace_id ASC`).all();
94261
+ return rows.map((row) => mapRow(row));
94262
+ }),
94263
+ catch: (error4) => {
94264
+ if (isCliError(error4))
94265
+ return error4;
94266
+ if (error4 instanceof StoreSchemaError)
94267
+ return mapStoreError(storeDbPath, error4);
94268
+ return new CliError({
94269
+ code: "QUEUE_UNAVAILABLE",
94270
+ message: "Store database is unavailable",
94271
+ exitCode: 1,
94272
+ details: { store_db: storeDbPath, error: String(error4?.message || error4) }
94273
+ });
94274
+ }
94275
+ }),
94276
+ upsert: (params3) => try_3({
94277
+ try: () => withStoreDb(params3.storeDbPath, (db) => {
94278
+ const workspaceId = normalizeText(params3.workspaceId, "workspaceId");
94279
+ const dbPath = normalizeText(params3.dbPath, "dbPath");
94280
+ const kbName = normalizeOptionalText(params3.kbName);
94281
+ const now2 = Math.max(1, Math.floor(Date.now()));
94282
+ const recordedAt = normalizeTimestamp(params3.recordedAt, now2);
94283
+ const verifiedAt = normalizeTimestamp(params3.verifiedAt, recordedAt);
94284
+ const lastUiContextAt = params3.lastUiContextAt === undefined ? undefined : normalizeTimestamp(params3.lastUiContextAt, recordedAt);
94285
+ const makeCurrent = params3.makeCurrent !== false;
94286
+ const tx = db.transaction(() => {
94287
+ const existing = db.prepare(`SELECT workspace_id, kb_name, db_path, source, is_current, first_seen_at, last_verified_at, last_ui_context_at, updated_at
94288
+ FROM workspace_bindings
94289
+ WHERE workspace_id = ?
94290
+ LIMIT 1`).get(workspaceId);
94291
+ if (makeCurrent) {
94292
+ db.prepare(`UPDATE workspace_bindings SET is_current = 0 WHERE workspace_id <> ? AND is_current = 1`).run(workspaceId);
94293
+ }
94294
+ const firstSeenAt = existing ? Number(existing.first_seen_at) : recordedAt;
94295
+ const effectiveKbName = kbName ?? (existing?.kb_name ?? undefined);
94296
+ const effectiveLastUiContextAt = lastUiContextAt ?? (existing?.last_ui_context_at === null ? undefined : existing?.last_ui_context_at ?? undefined);
94297
+ db.prepare(`INSERT INTO workspace_bindings(
94298
+ workspace_id, kb_name, db_path, source, is_current, first_seen_at, last_verified_at, last_ui_context_at, updated_at
94299
+ ) VALUES(
94300
+ @workspace_id, @kb_name, @db_path, @source, @is_current, @first_seen_at, @last_verified_at, @last_ui_context_at, @updated_at
94301
+ )
94302
+ ON CONFLICT(workspace_id) DO UPDATE SET
94303
+ kb_name = excluded.kb_name,
94304
+ db_path = excluded.db_path,
94305
+ source = excluded.source,
94306
+ is_current = excluded.is_current,
94307
+ last_verified_at = excluded.last_verified_at,
94308
+ last_ui_context_at = excluded.last_ui_context_at,
94309
+ updated_at = excluded.updated_at`).run({
94310
+ workspace_id: workspaceId,
94311
+ kb_name: effectiveKbName ?? null,
94312
+ db_path: dbPath,
94313
+ source: params3.source,
94314
+ is_current: makeCurrent ? 1 : 0,
94315
+ first_seen_at: firstSeenAt,
94316
+ last_verified_at: verifiedAt,
94317
+ last_ui_context_at: effectiveLastUiContextAt ?? null,
94318
+ updated_at: recordedAt
94319
+ });
94320
+ return mapRow(db.prepare(`SELECT workspace_id, kb_name, db_path, source, is_current, first_seen_at, last_verified_at, last_ui_context_at, updated_at
94321
+ FROM workspace_bindings
94322
+ WHERE workspace_id = ?
94323
+ LIMIT 1`).get(workspaceId));
94324
+ });
94325
+ return tx();
94326
+ }),
94327
+ catch: (error4) => {
94328
+ if (isCliError(error4))
94329
+ return error4;
94330
+ if (error4 instanceof StoreSchemaError)
94331
+ return mapStoreError(params3.storeDbPath, error4);
94332
+ return new CliError({
94333
+ code: "QUEUE_UNAVAILABLE",
94334
+ message: "Store database is unavailable",
94335
+ exitCode: 1,
94336
+ details: { store_db: params3.storeDbPath, error: String(error4?.message || error4) }
94337
+ });
94338
+ }
94339
+ })
94340
+ });
94341
+
94342
+ // src/lib/workspaceResolver.ts
94343
+ function normalizeText2(value8) {
94344
+ const normalized = String(value8 ?? "").trim();
94345
+ return normalized ? normalized : undefined;
94346
+ }
94347
+ function pathExists(filePath) {
94348
+ if (!filePath)
94349
+ return false;
94350
+ try {
94351
+ return fs18.statSync(filePath).isFile();
94352
+ } catch {
94353
+ return false;
94354
+ }
94355
+ }
94356
+ function resolvedFromBinding(params3) {
94357
+ return {
94358
+ resolved: true,
94359
+ workspaceId: params3.binding.workspaceId,
94360
+ dbPath: params3.binding.dbPath,
94361
+ source: params3.source,
94362
+ kbName: params3.binding.kbName,
94363
+ bindingSource: params3.binding.source,
94364
+ candidates: params3.candidates ?? [],
94365
+ reasons: params3.reasons ?? []
94366
+ };
94367
+ }
94368
+ function unresolved(params3) {
94369
+ return {
94370
+ resolved: false,
94371
+ source: "unresolved",
94372
+ candidates: params3.candidates,
94373
+ reasons: params3.reasons
94374
+ };
94375
+ }
94376
+ function buildWorkspaceResolveError(params3) {
94377
+ const hasRequestedWorkspace = Boolean(normalizeText2(params3.requestedWorkspaceId));
94378
+ return new CliError({
94379
+ code: hasRequestedWorkspace ? "DB_UNAVAILABLE" : "WORKSPACE_UNRESOLVED",
94380
+ message: hasRequestedWorkspace ? `Workspace database is unavailable: ${params3.requestedWorkspaceId}` : "Workspace is unresolved",
94381
+ exitCode: 1,
94382
+ details: {
94383
+ requested_workspace_id: params3.requestedWorkspaceId,
94384
+ candidates: params3.resolution.candidates,
94385
+ reasons: params3.resolution.reasons
94386
+ },
94387
+ hint: hasRequestedWorkspace ? [
94388
+ "Open the target KB once in RemNote so the host can refresh the binding.",
94389
+ "Verify that the expected remnote.db file still exists on the host."
94390
+ ] : [
94391
+ "Open the target KB once in RemNote so uiContext.kbId can establish a binding.",
94392
+ "Pass an explicit workspaceId or a RemNote deep link with /w/<workspaceId>/...."
94393
+ ]
94394
+ });
94395
+ }
94396
+ function resolveWorkspaceSnapshot(params3) {
94397
+ return gen2(function* () {
94398
+ const cfg = yield* AppConfig;
94399
+ const bindings = yield* WorkspaceBindings;
94400
+ const storeDbPath = cfg.storeDb;
94401
+ const recordedAt = Date.now();
94402
+ const requestedWorkspaceId = normalizeText2(params3.workspaceId);
94403
+ const linkWorkspaceId = params3.ref ? normalizeText2(tryParseRemnoteLinkFromRef(params3.ref)?.workspaceId) : undefined;
94404
+ const explicitWorkspaceId = requestedWorkspaceId ?? linkWorkspaceId;
94405
+ const explicitBindingSource = requestedWorkspaceId ? "explicit" : "deep_link";
94406
+ const loadCandidates = () => discoverWorkspaceCandidatesSync();
94407
+ if (explicitWorkspaceId) {
94408
+ const existing = yield* bindings.getByWorkspaceId({ storeDbPath, workspaceId: explicitWorkspaceId });
94409
+ if (existing && pathExists(existing.dbPath)) {
94410
+ const refreshed = yield* bindings.upsert({
94411
+ storeDbPath,
94412
+ workspaceId: existing.workspaceId,
94413
+ kbName: existing.kbName,
94414
+ dbPath: existing.dbPath,
94415
+ source: existing.source,
94416
+ makeCurrent: true,
94417
+ recordedAt,
94418
+ verifiedAt: recordedAt,
94419
+ lastUiContextAt: existing.lastUiContextAt
94420
+ });
94421
+ return resolvedFromBinding({ binding: refreshed, source: "explicit" });
94422
+ }
94423
+ const canonicalPath = tryResolveRemnoteDbPathForWorkspaceIdSync(explicitWorkspaceId);
94424
+ if (canonicalPath) {
94425
+ const upserted = yield* bindings.upsert({
94426
+ storeDbPath,
94427
+ workspaceId: explicitWorkspaceId,
94428
+ kbName: existing?.kbName,
94429
+ dbPath: canonicalPath,
94430
+ source: explicitBindingSource,
94431
+ makeCurrent: true,
94432
+ recordedAt,
94433
+ verifiedAt: recordedAt,
94434
+ lastUiContextAt: existing?.lastUiContextAt
94435
+ });
94436
+ return resolvedFromBinding({ binding: upserted, source: "explicit" });
94437
+ }
94438
+ return unresolved({
94439
+ candidates: loadCandidates(),
94440
+ reasons: [`No readable remnote.db found for workspace ${explicitWorkspaceId}`]
94441
+ });
94442
+ }
94443
+ const uiSnapshot = loadBridgeUiContextSnapshot({ stateFile: params3.stateFile, staleMs: params3.staleMs });
94444
+ let liveBinding;
94445
+ const liveKbId = uiSnapshot.status === "ok" ? normalizeText2(uiSnapshot.ui_context?.kbId) : undefined;
94446
+ if (liveKbId) {
94447
+ const liveDbPath = tryResolveRemnoteDbPathForWorkspaceIdSync(liveKbId);
94448
+ if (liveDbPath) {
94449
+ liveBinding = yield* bindings.upsert({
94450
+ storeDbPath,
94451
+ workspaceId: liveKbId,
94452
+ kbName: normalizeText2(uiSnapshot.ui_context?.kbName),
94453
+ dbPath: liveDbPath,
94454
+ source: "live_ui_context",
94455
+ makeCurrent: true,
94456
+ recordedAt,
94457
+ verifiedAt: recordedAt,
94458
+ lastUiContextAt: Number(uiSnapshot.ui_context?.updatedAt ?? recordedAt)
94459
+ });
94460
+ }
94461
+ }
94462
+ const current2 = yield* bindings.getCurrent({ storeDbPath });
94463
+ if (current2 && pathExists(current2.dbPath)) {
94464
+ const refreshed = yield* bindings.upsert({
94465
+ storeDbPath,
94466
+ workspaceId: current2.workspaceId,
94467
+ kbName: current2.kbName,
94468
+ dbPath: current2.dbPath,
94469
+ source: current2.source,
94470
+ makeCurrent: true,
94471
+ recordedAt,
94472
+ verifiedAt: recordedAt,
94473
+ lastUiContextAt: current2.lastUiContextAt
94474
+ });
94475
+ return resolvedFromBinding({
94476
+ binding: refreshed,
94477
+ source: liveBinding && liveBinding.workspaceId === refreshed.workspaceId ? "live_ui_context" : "binding"
94478
+ });
94479
+ }
94480
+ const candidates = loadCandidates();
94481
+ const primaryCandidates = candidates.filter((candidate) => candidate.kind === "primary");
94482
+ if (primaryCandidates.length === 1) {
94483
+ const only = primaryCandidates[0];
94484
+ const upserted = yield* bindings.upsert({
94485
+ storeDbPath,
94486
+ workspaceId: only.workspaceId,
94487
+ dbPath: only.dbPath,
94488
+ source: "single_candidate_auto",
94489
+ makeCurrent: true,
94490
+ recordedAt,
94491
+ verifiedAt: recordedAt
94492
+ });
94493
+ return resolvedFromBinding({ binding: upserted, source: "single_candidate_auto", candidates: [only] });
94494
+ }
94495
+ const reasons = [];
94496
+ if (current2 && !pathExists(current2.dbPath)) {
94497
+ reasons.push(`Current workspace binding points to a missing file: ${current2.dbPath}`);
94498
+ }
94499
+ if (!liveBinding) {
94500
+ reasons.push(uiSnapshot.status === "ok" ? "UI context did not provide a resolvable kbId" : `UI context is unavailable (${uiSnapshot.status})`);
94501
+ }
94502
+ if (primaryCandidates.length > 1) {
94503
+ reasons.push(`Multiple primary workspace candidates found: ${primaryCandidates.length}`);
94504
+ }
94505
+ if (primaryCandidates.length === 0) {
94506
+ reasons.push("No primary workspace candidates were discovered");
94507
+ }
94508
+ return unresolved({ candidates, reasons });
94509
+ });
94510
+ }
94511
+ function requireResolvedWorkspace(params3) {
94512
+ return resolveWorkspaceSnapshot(params3).pipe(flatMap9((resolution) => {
94513
+ if (resolution.resolved)
94514
+ return succeed8(resolution);
94515
+ const requestedWorkspaceId = normalizeText2(params3.workspaceId) ?? normalizeText2(tryParseRemnoteLinkFromRef(params3.ref ?? "")?.workspaceId);
94516
+ return fail8(buildWorkspaceResolveError({ requestedWorkspaceId, resolution }));
94517
+ }));
94518
+ }
94519
+
94520
+ // src/services/RefResolver.ts
94521
+ class RefResolver extends Tag2("RefResolver")() {
94522
+ }
94523
+ function stripQuotes(s) {
94524
+ const t = s.trim();
94525
+ if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
94526
+ return t.slice(1, -1);
94527
+ }
94528
+ return t;
94529
+ }
94530
+ function parseRef(input) {
94531
+ const raw4 = input.trim();
94532
+ const link3 = tryParseRemnoteLink(raw4);
94533
+ if (link3?.remId) {
94534
+ return { kind: "id", value: link3.remId };
94535
+ }
94536
+ const idx = raw4.indexOf(":");
94537
+ if (idx <= 0) {
94538
+ throw new CliError({
94539
+ code: "INVALID_ARGS",
94540
+ message: `Invalid ref: ${input}`,
94541
+ exitCode: 2,
94542
+ hint: [
94543
+ "Example: --ref id:xxx",
94544
+ 'Example: --ref "remnote://w/<workspaceId>/<remId>"',
94545
+ "Example: --ref page:Demo",
94546
+ "Example: --ref title:Demo",
94547
+ "Example: --ref daily:today",
94548
+ "Example: --ref daily:-1"
94549
+ ]
94550
+ });
94551
+ }
94552
+ const kind = raw4.slice(0, idx).trim();
94553
+ const value8 = stripQuotes(raw4.slice(idx + 1));
94554
+ if (!value8) {
94555
+ throw new CliError({
94556
+ code: "INVALID_ARGS",
94557
+ message: `Invalid ref (missing value): ${input}`,
94558
+ exitCode: 2
94559
+ });
94560
+ }
94561
+ if (kind === "id" || kind === "title" || kind === "daily") {
94562
+ if (kind === "id") {
94563
+ const link22 = tryParseRemnoteLink(value8);
94564
+ return { kind, value: link22?.remId ?? value8 };
94565
+ }
94566
+ return { kind, value: value8 };
94567
+ }
94568
+ if (kind === "page") {
94569
+ return { kind, value: value8 };
94570
+ }
94571
+ throw new CliError({
94572
+ code: "INVALID_ARGS",
94573
+ message: `Unsupported ref: ${input}`,
94574
+ exitCode: 2,
94575
+ hint: ["Supported prefixes: id:/page:/title:/daily:"]
94576
+ });
94577
+ }
94578
+ function parseDailyOffset(value8) {
94579
+ const v = value8.trim().toLowerCase();
94580
+ if (v === "today" || v === "now" || v === "0")
94581
+ return 0;
94582
+ if (v === "yesterday")
94583
+ return -1;
94584
+ if (v === "tomorrow")
94585
+ return 1;
94586
+ const n = Number.parseInt(v, 10);
94587
+ if (!Number.isFinite(n)) {
94588
+ throw new CliError({
94589
+ code: "INVALID_ARGS",
94590
+ message: `Invalid daily ref: ${value8} (expected today/yesterday/tomorrow or an integer offset)`,
94591
+ exitCode: 2
94592
+ });
94593
+ }
94594
+ return n;
94595
+ }
94596
+ function normalizeDbPathInput(dbPath) {
94597
+ const raw4 = typeof dbPath === "string" ? dbPath.trim() : "";
94598
+ if (!raw4)
94599
+ return;
94600
+ const expanded = raw4 === "~" ? homedir() : raw4.startsWith("~/") || raw4.startsWith("~\\") ? path24.join(homedir(), raw4.slice(2)) : raw4;
94601
+ return path24.resolve(path24.normalize(expanded));
94602
+ }
94603
+ var RefResolverLive = succeed10(RefResolver, {
94604
+ resolve: (ref, options6) => gen2(function* () {
94605
+ const cfg = yield* AppConfig;
94606
+ const parsed = yield* try_3({
94607
+ try: () => parseRef(ref),
94608
+ catch: (e) => e && typeof e === "object" && e._tag === "CliError" ? e : new CliError({ code: "INVALID_ARGS", message: `Invalid ref: ${ref}`, exitCode: 2 })
94609
+ });
94610
+ if (cfg.apiBaseUrl && parsed.kind !== "id") {
94611
+ return yield* fail8(remoteModeUnsupportedError({
94612
+ command: `ref resolution (${ref})`,
94613
+ reason: "this path still resolves refs by reading the local RemNote database",
94614
+ hints: [
94615
+ "Use a remote-capable command that accepts --ref and forwards it to the host API.",
94616
+ "If no remote endpoint exists yet, run the command on the host."
94617
+ ],
94618
+ apiBaseUrl: cfg.apiBaseUrl
94619
+ }));
94620
+ }
94621
+ if (parsed.kind === "id")
94622
+ return parsed.value;
94623
+ const normalizedDbPath = normalizeDbPathInput(options6?.dbPath) ?? normalizeDbPathInput(cfg.remnoteDb);
94624
+ const workspace = normalizedDbPath || cfg.apiBaseUrl ? undefined : yield* resolveWorkspaceSnapshot({ ref }).pipe(catchAll2(() => fail8(new CliError({
94625
+ code: "WORKSPACE_UNRESOLVED",
94626
+ message: `Workspace is unresolved for ref: ${ref}`,
94627
+ exitCode: 1
94628
+ }))));
94629
+ const dbPath = normalizedDbPath ?? workspace?.dbPath;
94630
+ if (!dbPath) {
94631
+ return yield* fail8(new CliError({
94632
+ code: "WORKSPACE_UNRESOLVED",
94633
+ message: `Workspace is unresolved for ref: ${ref}`,
94634
+ exitCode: 1
94635
+ }));
94636
+ }
94637
+ const dailyOffset = parsed.kind === "daily" ? yield* try_3({
94638
+ try: () => parseDailyOffset(parsed.value),
94639
+ catch: (e) => e && typeof e === "object" && e._tag === "CliError" ? e : new CliError({
94640
+ code: "INVALID_ARGS",
94641
+ message: `Invalid daily ref: ${parsed.value}`,
94642
+ exitCode: 2
94643
+ })
94644
+ }) : undefined;
94645
+ const queryInput = parsed.kind === "title" || parsed.kind === "page" ? { query: parsed.value } : { query: "date", useCurrentDate: true, dateOffsetDays: dailyOffset };
94646
+ const result = yield* tryPromise2({
94647
+ try: async () => await executeSearchRemOverview({
94648
+ ...queryInput,
94649
+ dbPath,
94650
+ limit: 1,
94651
+ preferExact: true,
94652
+ exactFirstSingle: true,
94653
+ pagesOnly: parsed.kind === "page" ? true : undefined
94654
+ }),
94655
+ catch: (e) => new CliError({
94656
+ code: "DB_UNAVAILABLE",
94657
+ message: String(e?.message || e || "RemNote DB is unavailable"),
94658
+ exitCode: 1
94659
+ })
94660
+ });
94661
+ const first3 = Array.isArray(result.matches) ? result.matches[0] : undefined;
94662
+ const id2 = first3?.id ? String(first3.id) : "";
94663
+ if (!id2) {
94664
+ return yield* fail8(new CliError({
94665
+ code: "INVALID_ARGS",
94666
+ message: `No Rem found for ref: ${ref}`,
94667
+ exitCode: 2
94668
+ }));
94669
+ }
94670
+ return id2;
94671
+ })
94672
+ });
93873
94673
 
93874
94674
  // src/commands/_resolveRefsInPayload.ts
93875
94675
  function shouldResolveRef(value8) {
@@ -93892,8 +94692,8 @@ function resolveRefsInPayload(params3) {
93892
94692
  const out = structuredClone(params3.payload);
93893
94693
  const resolvedRefCache = new Map;
93894
94694
  const idPaths = idFieldPathsForOpType(params3.opType);
93895
- for (const path24 of idPaths) {
93896
- const tokens = parsePathTokens(path24);
94695
+ for (const path25 of idPaths) {
94696
+ const tokens = parsePathTokens(path25);
93897
94697
  if (tokens.length === 0)
93898
94698
  continue;
93899
94699
  const leaves = collectLeafValues(out, tokens);
@@ -94063,274 +94863,187 @@ function compileApplyEnvelope(parsed) {
94063
94863
  ops,
94064
94864
  priority: parsed.priority,
94065
94865
  clientId: parsed.clientId,
94066
- idempotencyKey: parsed.idempotencyKey,
94067
- meta: parsed.meta,
94068
- notify: parsed.notify,
94069
- ensureDaemon: parsed.ensureDaemon
94070
- };
94071
- }
94072
- const compiled = yield* try_3({
94073
- try: () => compileWritePlanV1({
94074
- version: 1,
94075
- steps: parsed.actions.map((action) => ({
94076
- action: action.action,
94077
- input: action.input,
94078
- ...action.as ? { as: action.as } : {}
94079
- }))
94080
- }, { makeTempId }),
94081
- catch: (e) => isCliError(e) ? e : new CliError({
94082
- code: "INVALID_PAYLOAD",
94083
- message: String(e?.message || "Failed to compile apply actions"),
94084
- exitCode: 2
94085
- })
94086
- });
94087
- const resolvedOps = yield* forEach9(compiled.ops, (op) => resolveRefsInPayload({ opType: op.type, payload: op.payload }).pipe(map17((payload) => ({ ...op, payload }))), { concurrency: 1 });
94088
- const normalizedOps = yield* try_3({
94089
- try: () => resolvedOps.map((op) => normalizeOp(op, payloadSvc.normalizeKeys)),
94090
- catch: (e) => isCliError(e) ? e : new CliError({
94091
- code: "INVALID_PAYLOAD",
94092
- message: "Failed to generate ops",
94093
- exitCode: 2,
94094
- details: { error: String(e?.message || e) }
94095
- })
94096
- });
94097
- return {
94098
- kind: "actions",
94099
- ops: normalizedOps,
94100
- aliasMap: compiled.alias_map,
94101
- priority: parsed.priority,
94102
- clientId: parsed.clientId,
94103
- idempotencyKey: parsed.idempotencyKey,
94104
- meta: parsed.meta,
94105
- notify: parsed.notify,
94106
- ensureDaemon: parsed.ensureDaemon
94107
- };
94108
- });
94109
- }
94110
-
94111
- // src/commands/read/selection/_shared.ts
94112
- function normalizeId(raw4) {
94113
- return typeof raw4 === "string" ? raw4.trim() : "";
94114
- }
94115
- function parseSelectionInfo(raw4) {
94116
- const kindRaw = typeof raw4?.kind === "string" ? raw4.kind.trim() : "";
94117
- const selectionType = typeof raw4?.selectionType === "string" ? raw4.selectionType : undefined;
94118
- const updatedAtRaw = Number(raw4?.updatedAt ?? 0);
94119
- const updatedAt = Number.isFinite(updatedAtRaw) && updatedAtRaw > 0 ? updatedAtRaw : 0;
94120
- if (kindRaw === "text" || kindRaw === "" && selectionType === "Text") {
94121
- const remId = normalizeId(raw4?.remId);
94122
- const startRaw = raw4?.range?.start;
94123
- const endRaw = raw4?.range?.end;
94124
- const start4 = typeof startRaw === "number" && Number.isFinite(startRaw) ? Math.floor(startRaw) : NaN;
94125
- const end6 = typeof endRaw === "number" && Number.isFinite(endRaw) ? Math.floor(endRaw) : NaN;
94126
- const isReverse = raw4?.isReverse === true;
94127
- if (remId && Number.isFinite(start4) && Number.isFinite(end6) && start4 !== end6) {
94128
- return { kind: "text", selectionType, remId, range: { start: start4, end: end6 }, isReverse, updatedAt };
94129
- }
94130
- return { kind: "none", selectionType: undefined, updatedAt };
94131
- }
94132
- if (kindRaw === "rem" || kindRaw === "" || selectionType === "Rem") {
94133
- const totalCountRaw = Number(raw4?.totalCount ?? 0);
94134
- const remIds = Array.isArray(raw4?.remIds) ? raw4.remIds.map(normalizeId).filter(Boolean) : [];
94135
- const totalCount = Number.isFinite(totalCountRaw) && totalCountRaw >= 0 ? Math.floor(totalCountRaw) : remIds.length;
94136
- const truncated = !!raw4?.truncated || totalCount > remIds.length;
94137
- if (remIds.length > 0 || totalCount > 0) {
94138
- return { kind: "rem", selectionType, totalCount, truncated, remIds, updatedAt };
94139
- }
94140
- }
94141
- return { kind: "none", selectionType: undefined, updatedAt };
94142
- }
94143
- function loadBridgeSelectionSnapshot(params3) {
94144
- const resolved = resolveStateFilePath2(params3.stateFile);
94145
- const stateFilePath = resolved.path;
94146
- const now2 = Date.now();
94147
- const staleThreshold = resolveStaleMs2(params3.staleMs);
94148
- if (resolved.disabled) {
94149
- return {
94150
- status: "off",
94151
- state_file: stateFilePath,
94152
- updatedAt: 0,
94153
- now: now2,
94154
- stale_ms: staleThreshold,
94155
- clients: 0
94156
- };
94157
- }
94158
- const state = readJson2(stateFilePath);
94159
- if (!state) {
94160
- return {
94161
- status: "down",
94162
- state_file: stateFilePath,
94163
- updatedAt: 0,
94164
- now: now2,
94165
- stale_ms: staleThreshold,
94166
- clients: 0
94167
- };
94168
- }
94169
- const updatedAt = Number(state.updatedAt ?? 0);
94170
- const isStale = !Number.isFinite(updatedAt) || updatedAt <= 0 || now2 - updatedAt > staleThreshold;
94171
- const clients = Array.isArray(state.clients) ? state.clients : [];
94172
- const activeConnIdRaw = typeof state.activeWorkerConnId === "string" ? state.activeWorkerConnId.trim() : "";
94173
- const client = pickClient2(clients, params3.connId || activeConnIdRaw || undefined);
94174
- if (!client) {
94175
- return {
94176
- status: "no_client",
94177
- state_file: stateFilePath,
94178
- updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
94179
- now: now2,
94180
- stale_ms: staleThreshold,
94181
- clients: clients.length
94182
- };
94183
- }
94184
- const selection = parseSelectionInfo(client.selection);
94185
- if (isStale) {
94186
- return {
94187
- status: "stale",
94188
- state_file: stateFilePath,
94189
- updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
94190
- now: now2,
94191
- stale_ms: staleThreshold,
94192
- clients: clients.length,
94193
- selection
94194
- };
94195
- }
94196
- return {
94197
- status: "ok",
94198
- state_file: stateFilePath,
94199
- updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
94200
- now: now2,
94201
- stale_ms: staleThreshold,
94202
- clients: clients.length,
94203
- selection
94204
- };
94205
- }
94206
- function requireOkSelection(snapshot2) {
94207
- if (snapshot2.status === "ok" && snapshot2.selection) {
94208
- return succeed8(snapshot2.selection);
94209
- }
94210
- const msg = snapshot2.status === "off" ? "WS state is disabled (REMNOTE_WS_STATE_FILE=0)" : snapshot2.status === "down" ? "WS state file not found: the daemon may not be running or has not written the state file yet" : snapshot2.status === "stale" ? "WS state is stale: the daemon may have stopped or has not updated for a long time" : snapshot2.status === "no_client" ? "No RemNote client is currently connected to the daemon" : "Selection is unavailable";
94211
- return fail8(new CliError({
94212
- code: "WS_UNAVAILABLE",
94213
- message: msg,
94214
- exitCode: 1,
94215
- details: snapshot2
94216
- }));
94217
- }
94218
- function requireOkRemSelection(snapshot2) {
94219
- return requireOkSelection(snapshot2).pipe(flatMap9((sel) => {
94220
- if (sel.kind === "rem")
94221
- return succeed8(sel);
94222
- if (sel.kind === "text") {
94223
- return fail8(new CliError({
94224
- code: "INVALID_ARGS",
94225
- message: "Current selection is text; this command requires Rem selection",
94226
- exitCode: 2,
94227
- details: snapshot2
94228
- }));
94229
- }
94230
- return fail8(new CliError({
94231
- code: "INVALID_ARGS",
94232
- message: "No Rem is currently selected",
94233
- exitCode: 2,
94234
- details: snapshot2
94235
- }));
94236
- }));
94237
- }
94238
-
94239
- // src/commands/read/uiContext/_shared.ts
94240
- function loadBridgeUiContextSnapshot(params3) {
94241
- const resolved = resolveStateFilePath2(params3.stateFile);
94242
- const stateFilePath = resolved.path;
94243
- const now2 = Date.now();
94244
- const staleThreshold = resolveStaleMs2(params3.staleMs);
94245
- if (resolved.disabled) {
94246
- return {
94247
- status: "off",
94248
- state_file: stateFilePath,
94249
- updatedAt: 0,
94250
- now: now2,
94251
- stale_ms: staleThreshold,
94252
- clients: 0
94253
- };
94254
- }
94255
- const state = readJson2(stateFilePath);
94256
- if (!state) {
94257
- return {
94258
- status: "down",
94259
- state_file: stateFilePath,
94260
- updatedAt: 0,
94261
- now: now2,
94262
- stale_ms: staleThreshold,
94263
- clients: 0
94264
- };
94265
- }
94266
- const updatedAt = Number(state.updatedAt ?? 0);
94267
- const isStale = !Number.isFinite(updatedAt) || updatedAt <= 0 || now2 - updatedAt > staleThreshold;
94268
- const clients = Array.isArray(state.clients) ? state.clients : [];
94269
- const activeConnIdRaw = typeof state.activeWorkerConnId === "string" ? state.activeWorkerConnId.trim() : "";
94270
- const client = pickClient2(clients, params3.connId || activeConnIdRaw || undefined);
94271
- if (!client) {
94272
- return {
94273
- status: "no_client",
94274
- state_file: stateFilePath,
94275
- updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
94276
- now: now2,
94277
- stale_ms: staleThreshold,
94278
- clients: clients.length
94279
- };
94280
- }
94281
- const url2 = typeof client.uiContext?.url === "string" ? client.uiContext.url : "";
94282
- const paneId = typeof client.uiContext?.paneId === "string" ? client.uiContext.paneId : "";
94283
- const pageRemId = typeof client.uiContext?.pageRemId === "string" ? client.uiContext.pageRemId : "";
94284
- const focusedRemId = typeof client.uiContext?.focusedRemId === "string" ? client.uiContext.focusedRemId : "";
94285
- const focusedPortalId = typeof client.uiContext?.focusedPortalId === "string" ? client.uiContext.focusedPortalId : "";
94286
- const kbId = typeof client.uiContext?.kbId === "string" ? client.uiContext.kbId : undefined;
94287
- const kbName = typeof client.uiContext?.kbName === "string" ? client.uiContext.kbName : undefined;
94288
- const source = typeof client.uiContext?.source === "string" ? client.uiContext.source : undefined;
94289
- const ctxUpdatedAtRaw = Number(client.uiContext?.updatedAt ?? 0);
94290
- const ctxUpdatedAt = Number.isFinite(ctxUpdatedAtRaw) && ctxUpdatedAtRaw > 0 ? ctxUpdatedAtRaw : 0;
94291
- const uiContext = {
94292
- url: url2,
94293
- paneId,
94294
- pageRemId,
94295
- focusedRemId,
94296
- focusedPortalId,
94297
- kbId,
94298
- kbName,
94299
- source,
94300
- updatedAt: ctxUpdatedAt
94301
- };
94302
- if (isStale) {
94866
+ idempotencyKey: parsed.idempotencyKey,
94867
+ meta: parsed.meta,
94868
+ notify: parsed.notify,
94869
+ ensureDaemon: parsed.ensureDaemon
94870
+ };
94871
+ }
94872
+ const compiled = yield* try_3({
94873
+ try: () => compileWritePlanV1({
94874
+ version: 1,
94875
+ steps: parsed.actions.map((action) => ({
94876
+ action: action.action,
94877
+ input: action.input,
94878
+ ...action.as ? { as: action.as } : {}
94879
+ }))
94880
+ }, { makeTempId }),
94881
+ catch: (e) => isCliError(e) ? e : new CliError({
94882
+ code: "INVALID_PAYLOAD",
94883
+ message: String(e?.message || "Failed to compile apply actions"),
94884
+ exitCode: 2
94885
+ })
94886
+ });
94887
+ const resolvedOps = yield* forEach9(compiled.ops, (op) => resolveRefsInPayload({ opType: op.type, payload: op.payload }).pipe(map17((payload) => ({ ...op, payload }))), { concurrency: 1 });
94888
+ const normalizedOps = yield* try_3({
94889
+ try: () => resolvedOps.map((op) => normalizeOp(op, payloadSvc.normalizeKeys)),
94890
+ catch: (e) => isCliError(e) ? e : new CliError({
94891
+ code: "INVALID_PAYLOAD",
94892
+ message: "Failed to generate ops",
94893
+ exitCode: 2,
94894
+ details: { error: String(e?.message || e) }
94895
+ })
94896
+ });
94303
94897
  return {
94304
- status: "stale",
94305
- state_file: stateFilePath,
94306
- updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
94307
- now: now2,
94308
- stale_ms: staleThreshold,
94309
- clients: clients.length,
94310
- ui_context: uiContext
94898
+ kind: "actions",
94899
+ ops: normalizedOps,
94900
+ aliasMap: compiled.alias_map,
94901
+ priority: parsed.priority,
94902
+ clientId: parsed.clientId,
94903
+ idempotencyKey: parsed.idempotencyKey,
94904
+ meta: parsed.meta,
94905
+ notify: parsed.notify,
94906
+ ensureDaemon: parsed.ensureDaemon
94311
94907
  };
94312
- }
94313
- return {
94314
- status: "ok",
94315
- state_file: stateFilePath,
94316
- updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0,
94317
- now: now2,
94318
- stale_ms: staleThreshold,
94319
- clients: clients.length,
94320
- ui_context: uiContext
94321
- };
94908
+ });
94322
94909
  }
94323
- function requireOkUiContext(snapshot2) {
94324
- if (snapshot2.status === "ok" && snapshot2.ui_context) {
94325
- return succeed8(snapshot2.ui_context);
94910
+
94911
+ // src/commands/write/_optionRuntimeGuard.ts
94912
+ function optionCapabilityHint(scopeLabel) {
94913
+ return [
94914
+ `Use the RemNote UI to convert the ${scopeLabel} property to single_select or multi_select first.`,
94915
+ `Only then use ${scopeLabel} option add/remove from the CLI.`,
94916
+ "Plain properties do not support option mutation."
94917
+ ];
94918
+ }
94919
+ function isSupportedOptionFieldType(value8) {
94920
+ return value8 === "single_select" || value8 === "multi_select";
94921
+ }
94922
+ function invalidOptionTargetError(params3) {
94923
+ if (params3.targetKind === "option") {
94924
+ return new CliError({
94925
+ code: "INVALID_ARGS",
94926
+ message: `Option mutation requires option ${params3.targetId} to belong to a single_select or multi_select ${params3.scopeLabel} property`,
94927
+ exitCode: 2,
94928
+ details: {
94929
+ scope: params3.scopeLabel,
94930
+ option_id: params3.targetId,
94931
+ parent_property_id: params3.parentPropertyId ?? undefined,
94932
+ property_field_type: params3.fieldType ?? undefined
94933
+ },
94934
+ hint: optionCapabilityHint(params3.scopeLabel)
94935
+ });
94326
94936
  }
94327
- const msg = snapshot2.status === "off" ? "WS state is disabled (REMNOTE_WS_STATE_FILE=0)" : snapshot2.status === "down" ? "WS state file not found: the daemon may not be running or has not written the state file yet" : snapshot2.status === "stale" ? "WS state is stale: the daemon may have stopped or has not updated for a long time" : snapshot2.status === "no_client" ? "No RemNote client is currently connected to the daemon" : "UI context is unavailable";
94328
- return fail8(new CliError({
94329
- code: "WS_UNAVAILABLE",
94330
- message: msg,
94331
- exitCode: 1,
94332
- details: snapshot2
94333
- }));
94937
+ return new CliError({
94938
+ code: "INVALID_ARGS",
94939
+ message: `Option mutation requires property ${params3.targetId} to have ft=single_select or ft=multi_select in the local RemNote DB`,
94940
+ exitCode: 2,
94941
+ details: {
94942
+ scope: params3.scopeLabel,
94943
+ property_id: params3.targetId,
94944
+ property_field_type: params3.fieldType ?? undefined
94945
+ },
94946
+ hint: optionCapabilityHint(params3.scopeLabel)
94947
+ });
94948
+ }
94949
+ function missingOptionTargetError(params3) {
94950
+ return new CliError({
94951
+ code: "INVALID_ARGS",
94952
+ message: `${params3.targetKind === "property" ? "Property" : "Option"} ${params3.targetId} was not found in the local RemNote DB`,
94953
+ exitCode: 2,
94954
+ details: {
94955
+ scope: params3.scopeLabel,
94956
+ [`${params3.targetKind}_id`]: params3.targetId
94957
+ },
94958
+ hint: optionCapabilityHint(params3.scopeLabel)
94959
+ });
94960
+ }
94961
+ function ensureOptionMutationSupportedForProperty(params3) {
94962
+ return gen2(function* () {
94963
+ const remDb = yield* RemDb;
94964
+ const cfg = yield* AppConfig;
94965
+ const propertyId = String(params3.propertyId ?? "").trim();
94966
+ if (!propertyId) {
94967
+ return yield* fail8(new CliError({
94968
+ code: "INVALID_ARGS",
94969
+ message: "Missing property id for option mutation",
94970
+ exitCode: 2
94971
+ }));
94972
+ }
94973
+ const result = yield* remDb.withDb(cfg.remnoteDb, (db) => {
94974
+ const row = db.prepare(`SELECT json_extract(doc, '$.ft') AS field_type
94975
+ FROM quanta
94976
+ WHERE _id = ?`).get(propertyId);
94977
+ return { found: !!row, fieldType: typeof row?.field_type === "string" ? row.field_type : null };
94978
+ });
94979
+ if (!result.result.found) {
94980
+ return yield* fail8(missingOptionTargetError({ scopeLabel: params3.scopeLabel, targetKind: "property", targetId: propertyId }));
94981
+ }
94982
+ if (!isSupportedOptionFieldType(result.result.fieldType)) {
94983
+ return yield* fail8(invalidOptionTargetError({
94984
+ scopeLabel: params3.scopeLabel,
94985
+ targetKind: "property",
94986
+ targetId: propertyId,
94987
+ fieldType: result.result.fieldType
94988
+ }));
94989
+ }
94990
+ });
94991
+ }
94992
+ function ensureOptionMutationSupportedForOption(params3) {
94993
+ return gen2(function* () {
94994
+ const remDb = yield* RemDb;
94995
+ const cfg = yield* AppConfig;
94996
+ const optionId = String(params3.optionId ?? "").trim();
94997
+ if (!optionId) {
94998
+ return yield* fail8(new CliError({
94999
+ code: "INVALID_ARGS",
95000
+ message: "Missing option id for option mutation",
95001
+ exitCode: 2
95002
+ }));
95003
+ }
95004
+ const result = yield* remDb.withDb(cfg.remnoteDb, (db) => {
95005
+ const optionRow = db.prepare(`SELECT json_extract(doc, '$.parent') AS parent_property_id
95006
+ FROM quanta
95007
+ WHERE _id = ?`).get(optionId);
95008
+ const parentPropertyId = typeof optionRow?.parent_property_id === "string" ? optionRow.parent_property_id.trim() : "";
95009
+ if (!parentPropertyId) {
95010
+ return { found: !!optionRow, parentPropertyId: null, fieldType: null };
95011
+ }
95012
+ const propertyRow = db.prepare(`SELECT json_extract(doc, '$.ft') AS field_type
95013
+ FROM quanta
95014
+ WHERE _id = ?`).get(parentPropertyId);
95015
+ return {
95016
+ found: !!optionRow,
95017
+ parentPropertyId,
95018
+ fieldType: typeof propertyRow?.field_type === "string" ? propertyRow.field_type : null
95019
+ };
95020
+ });
95021
+ if (!result.result.found) {
95022
+ return yield* fail8(missingOptionTargetError({ scopeLabel: params3.scopeLabel, targetKind: "option", targetId: optionId }));
95023
+ }
95024
+ if (!isSupportedOptionFieldType(result.result.fieldType)) {
95025
+ return yield* fail8(invalidOptionTargetError({
95026
+ scopeLabel: params3.scopeLabel,
95027
+ targetKind: "option",
95028
+ targetId: optionId,
95029
+ parentPropertyId: result.result.parentPropertyId,
95030
+ fieldType: result.result.fieldType
95031
+ }));
95032
+ }
95033
+ });
95034
+ }
95035
+ function validateOptionMutationOps(params3) {
95036
+ return forEach9(params3.ops, (op) => {
95037
+ if (op.type === "add_option") {
95038
+ const propertyId = typeof op.payload?.property_id === "string" ? op.payload.property_id : "";
95039
+ return ensureOptionMutationSupportedForProperty({ scopeLabel: params3.scopeLabel, propertyId });
95040
+ }
95041
+ if (op.type === "remove_option") {
95042
+ const optionId = typeof op.payload?.option_id === "string" ? op.payload.option_id : "";
95043
+ return ensureOptionMutationSupportedForOption({ scopeLabel: params3.scopeLabel, optionId });
95044
+ }
95045
+ return _void;
95046
+ }, { concurrency: 1, discard: true });
94334
95047
  }
94335
95048
 
94336
95049
  // src/commands/_waitTxn.ts
@@ -94464,14 +95177,6 @@ function waitForTxn(params3) {
94464
95177
  });
94465
95178
  }
94466
95179
 
94467
- // src/lib/apiUrls.ts
94468
- function apiLocalBaseUrl(port3) {
94469
- return `http://127.0.0.1:${port3}`;
94470
- }
94471
- function apiContainerBaseUrl(port3) {
94472
- return `http://host.docker.internal:${port3}`;
94473
- }
94474
-
94475
95180
  // src/lib/text.ts
94476
95181
  function trimBoundaryBlankLines(input) {
94477
95182
  const normalized = input.replace(/\r\n?/g, `
@@ -94512,7 +95217,7 @@ function clampInt3(value8, min5, max7) {
94512
95217
  return min5;
94513
95218
  return Math.max(min5, Math.min(max7, Math.floor(value8)));
94514
95219
  }
94515
- function parseDateInput2(raw4) {
95220
+ function parseDateInput(raw4) {
94516
95221
  const trimmed2 = raw4.trim();
94517
95222
  const match24 = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed2);
94518
95223
  const value8 = match24 ? new Date(Number(match24[1]), Number(match24[2]) - 1, Number(match24[3])) : new Date(trimmed2);
@@ -94532,10 +95237,11 @@ function executeDbSearchUseCase(params3) {
94532
95237
  return gen2(function* () {
94533
95238
  const cfg = yield* AppConfig;
94534
95239
  const effectiveTimeoutMs = clampInt3(params3.timeoutMs ?? 30000, 1, 30000);
95240
+ const workspace = cfg.remnoteDb ? undefined : yield* requireResolvedWorkspace({});
94535
95241
  return yield* tryPromise2({
94536
95242
  try: async () => await executeSearchRemOverview({
94537
95243
  query: params3.query,
94538
- dbPath: cfg.remnoteDb,
95244
+ dbPath: cfg.remnoteDb ?? workspace.dbPath,
94539
95245
  timeRange: params3.timeRange,
94540
95246
  parentId: params3.parentId,
94541
95247
  pagesOnly: params3.pagesOnly,
@@ -94585,19 +95291,20 @@ function executePluginSearchUseCase(params3) {
94585
95291
  }
94586
95292
  function executeReadOutlineUseCase(params3) {
94587
95293
  return gen2(function* () {
94588
- const cfg = yield* AppConfig;
94589
95294
  const refs = yield* RefResolver;
95295
+ const cfg = yield* AppConfig;
94590
95296
  if (params3.id && params3.ref) {
94591
95297
  return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "Choose only one of --id or --ref", exitCode: 2 }));
94592
95298
  }
94593
- const resolvedId = params3.ref ? yield* refs.resolve(params3.ref) : params3.id;
95299
+ const workspace = cfg.remnoteDb ? undefined : yield* requireResolvedWorkspace({ ref: params3.ref });
95300
+ const resolvedId = params3.ref ? yield* refs.resolve(params3.ref, { dbPath: cfg.remnoteDb ?? workspace?.dbPath }) : params3.id;
94594
95301
  if (!resolvedId) {
94595
95302
  return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "You must provide --id or --ref", exitCode: 2 }));
94596
95303
  }
94597
95304
  return yield* tryPromise2({
94598
95305
  try: async () => await executeOutlineRemSubtree({
94599
95306
  id: resolvedId,
94600
- dbPath: cfg.remnoteDb,
95307
+ dbPath: cfg.remnoteDb ?? workspace.dbPath,
94601
95308
  maxDepth: params3.depth,
94602
95309
  startOffset: params3.offset,
94603
95310
  maxNodes: params3.nodes,
@@ -94617,22 +95324,24 @@ function executeDailyRemIdUseCase(params3) {
94617
95324
  if (params3.date && params3.offsetDays !== undefined) {
94618
95325
  return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "Choose only one of --date or --offset-days", exitCode: 2 }));
94619
95326
  }
94620
- const cfg = yield* AppConfig;
94621
95327
  const refs = yield* RefResolver;
94622
95328
  const remDb = yield* RemDb;
95329
+ const cfg = yield* AppConfig;
95330
+ const workspace = cfg.remnoteDb ? undefined : yield* requireResolvedWorkspace({});
95331
+ const dbPath = cfg.remnoteDb ?? workspace.dbPath;
94623
95332
  let ref = `daily:${params3.offsetDays ?? 0}`;
94624
95333
  let remId = "";
94625
95334
  let dateString;
94626
95335
  if (params3.date) {
94627
- const target2 = parseDateInput2(params3.date);
94628
- dateString = yield* remDb.withDb(cfg.remnoteDb, async (db) => {
95336
+ const target2 = parseDateInput(params3.date);
95337
+ dateString = yield* remDb.withDb(dbPath, async (db) => {
94629
95338
  const format9 = await getDateFormatting(db) ?? "yyyy/MM/dd";
94630
95339
  return formatDateWithPattern(target2, format9);
94631
95340
  }).pipe(map17((result2) => result2.result), catchAll2(() => succeed8(formatDateWithPattern(target2, "yyyy/MM/dd"))));
94632
95341
  const result = yield* tryPromise2({
94633
95342
  try: async () => await executeSearchRemOverview({
94634
95343
  query: dateString,
94635
- dbPath: cfg.remnoteDb,
95344
+ dbPath,
94636
95345
  limit: 1,
94637
95346
  preferExact: true,
94638
95347
  exactFirstSingle: true,
@@ -94653,10 +95362,10 @@ function executeDailyRemIdUseCase(params3) {
94653
95362
  } else {
94654
95363
  const offset = params3.offsetDays ?? 0;
94655
95364
  ref = `daily:${offset}`;
94656
- remId = yield* refs.resolve(ref);
95365
+ remId = yield* refs.resolve(ref, { dbPath });
94657
95366
  const target2 = todayAtMidnight();
94658
95367
  target2.setDate(target2.getDate() + offset);
94659
- dateString = yield* remDb.withDb(cfg.remnoteDb, async (db) => {
95368
+ dateString = yield* remDb.withDb(dbPath, async (db) => {
94660
95369
  const format9 = await getDateFormatting(db) ?? "yyyy/MM/dd";
94661
95370
  return formatDateWithPattern(target2, format9);
94662
95371
  }).pipe(map17((result) => result.result), catchAll2(() => succeed8(undefined)));
@@ -94666,6 +95375,7 @@ function executeDailyRemIdUseCase(params3) {
94666
95375
  }
94667
95376
  function executeWriteApplyUseCase(params3) {
94668
95377
  return gen2(function* () {
95378
+ const cfg = yield* AppConfig;
94669
95379
  const payloadSvc = yield* Payload;
94670
95380
  const parsed = yield* try_3({
94671
95381
  try: () => parseApplyEnvelope(payloadSvc.normalizeKeys(params3.raw)),
@@ -94676,6 +95386,7 @@ function executeWriteApplyUseCase(params3) {
94676
95386
  })
94677
95387
  });
94678
95388
  const compiled = yield* compileApplyEnvelope(parsed);
95389
+ yield* validateOptionMutationOps({ scopeLabel: "generic", ops: compiled.ops });
94679
95390
  if (compiled.ops.length > 500) {
94680
95391
  return yield* fail8(new CliError({
94681
95392
  code: "PAYLOAD_TOO_LARGE",
@@ -94731,9 +95442,13 @@ function collectApiHealthUseCase(params3) {
94731
95442
  wsUrl: cfg.wsUrl
94732
95443
  },
94733
95444
  activeWorkerConnId: clientsRes._tag === "Right" ? clientsRes.right.activeWorkerConnId ?? null : null,
94734
- queue: queueStats2._tag === "Right" ? { pending: queueStats2.right.pending ?? 0, in_flight: queueStats2.right.in_flight ?? 0 } : { pending: 0, in_flight: 0 },
94735
- localBaseUrl: apiLocalBaseUrl(params3.port),
94736
- containerBaseUrl: apiContainerBaseUrl(params3.port),
95445
+ queue: queueStats2._tag === "Right" ? {
95446
+ available: true,
95447
+ pending: queueStats2.right.pending ?? 0,
95448
+ in_flight: queueStats2.right.in_flight ?? 0
95449
+ } : { available: false, pending: 0, in_flight: 0 },
95450
+ localBaseUrl: apiLocalBaseUrl(params3.port, params3.basePath),
95451
+ containerBaseUrl: apiContainerBaseUrl(params3.port, params3.basePath),
94737
95452
  basePath: params3.basePath,
94738
95453
  host: params3.host,
94739
95454
  port: params3.port
@@ -94742,17 +95457,50 @@ function collectApiHealthUseCase(params3) {
94742
95457
  }
94743
95458
  function collectApiStatusUseCase(params3) {
94744
95459
  return gen2(function* () {
95460
+ const cfg = yield* AppConfig;
94745
95461
  const health = yield* collectApiHealthUseCase(params3);
94746
95462
  const uiContext = loadBridgeUiContextSnapshot({ stateFile: undefined, staleMs: undefined, connId: undefined });
94747
95463
  const selection = loadBridgeSelectionSnapshot({ stateFile: undefined, staleMs: undefined, connId: undefined });
95464
+ const workspaceResolution = yield* resolveWorkspaceSnapshot({});
95465
+ const pluginRpcReady = health.daemon.healthy === true && typeof health.activeWorkerConnId === "string" && health.activeWorkerConnId.length > 0;
95466
+ const uiSessionReady = uiContext.status === "ok" || selection.status === "ok";
95467
+ const effectiveDbPath = workspaceResolution.dbPath ?? cfg.remnoteDb ?? null;
95468
+ const dbReadReady = workspaceResolution.resolved === true || !!cfg.remnoteDb;
95469
+ const queueReady = health.queue?.available === true;
95470
+ const writeReady = queueReady && pluginRpcReady;
95471
+ const bindingSource = workspaceResolution.bindingSource ?? (cfg.remnoteDb ? "config" : workspaceResolution.source === "unresolved" ? undefined : workspaceResolution.source);
94748
95472
  return {
94749
95473
  ...health,
95474
+ capabilities: {
95475
+ db_read_ready: dbReadReady,
95476
+ plugin_rpc_ready: pluginRpcReady,
95477
+ write_ready: writeReady,
95478
+ ui_session_ready: uiSessionReady
95479
+ },
95480
+ workspace: {
95481
+ resolved: workspaceResolution.resolved,
95482
+ currentWorkspaceId: workspaceResolution.workspaceId ?? null,
95483
+ currentDbPath: effectiveDbPath,
95484
+ bindingSource: bindingSource ?? null,
95485
+ resolutionSource: cfg.remnoteDb && workspaceResolution.resolved !== true ? "config" : workspaceResolution.source,
95486
+ candidateWorkspaces: workspaceResolution.candidates,
95487
+ reasons: workspaceResolution.reasons
95488
+ },
95489
+ plugin: {
95490
+ ws_healthy: health.daemon.healthy,
95491
+ active_worker_conn_id: health.activeWorkerConnId
95492
+ },
95493
+ write: {
95494
+ daemon_ready: health.daemon.healthy,
95495
+ queue_ready: queueReady,
95496
+ worker_ready: pluginRpcReady
95497
+ },
94750
95498
  uiContext,
94751
95499
  selection
94752
95500
  };
94753
95501
  });
94754
95502
  }
94755
- function normalizeText(value8) {
95503
+ function normalizeText3(value8) {
94756
95504
  return typeof value8 === "string" ? value8.trim() : "";
94757
95505
  }
94758
95506
  function truncateText(value8, maxLength) {
@@ -94764,8 +95512,8 @@ function truncateText(value8, maxLength) {
94764
95512
  return `${normalized.slice(0, maxLength)}…`;
94765
95513
  }
94766
95514
  function pickTitle(kt, ke, r) {
94767
- const combined = [kt, ke].map(normalizeText).filter(Boolean).join(" | ");
94768
- const raw4 = combined || normalizeText(r);
95515
+ const combined = [kt, ke].map(normalizeText3).filter(Boolean).join(" | ");
95516
+ const raw4 = combined || normalizeText3(r);
94769
95517
  if (!raw4)
94770
95518
  return "";
94771
95519
  const normalized = raw4.replace(/\s+/g, " ").trim();
@@ -94875,8 +95623,8 @@ function collectPluginCurrentUseCase(params3) {
94875
95623
  const selectionSnapshot = loadBridgeSelectionSnapshot({ stateFile: params3.stateFile, staleMs: params3.staleMs });
94876
95624
  const ui = uiSnapshot.ui_context;
94877
95625
  const selection = selectionSnapshot.selection;
94878
- const pageRemId = normalizeText(ui?.pageRemId);
94879
- const focusedRemId = normalizeText(ui?.focusedRemId);
95626
+ const pageRemId = normalizeText3(ui?.pageRemId);
95627
+ const focusedRemId = normalizeText3(ui?.focusedRemId);
94880
95628
  const selectionKind = selection?.kind ?? "none";
94881
95629
  const selectionIds = selection?.kind === "rem" ? uniqueNonEmpty(selection.remIds.map(String)) : selection?.kind === "text" ? uniqueNonEmpty([selection.remId]) : [];
94882
95630
  const selectionTotalCountRaw = selection?.kind === "rem" ? Number(selection.totalCount ?? 0) : selection?.kind === "text" ? 1 : 0;
@@ -94888,10 +95636,11 @@ function collectPluginCurrentUseCase(params3) {
94888
95636
  const selectionShownIds = selectionIds.slice(0, selectionLimitEffective);
94889
95637
  const idsToResolve = uniqueNonEmpty([currentId, pageRemId, focusedRemId, ...selectionShownIds]);
94890
95638
  const warnings = [];
94891
- const dbPathCandidate = cfg.remnoteDb || (() => {
94892
- const kbId = normalizeText(ui?.kbId);
94893
- return kbId ? remnoteDbPathForWorkspaceId(kbId) : undefined;
94894
- })();
95639
+ const workspace = yield* resolveWorkspaceSnapshot({
95640
+ stateFile: params3.stateFile,
95641
+ staleMs: params3.staleMs
95642
+ }).pipe(catchAll2(() => succeed8(undefined)));
95643
+ const dbPathCandidate = cfg.remnoteDb ?? (workspace?.resolved ? workspace.dbPath : undefined);
94895
95644
  const titles = yield* gen2(function* () {
94896
95645
  if (!dbPathCandidate || idsToResolve.length === 0)
94897
95646
  return new Map;
@@ -94930,15 +95679,16 @@ function collectSelectionCurrentUseCase(params3) {
94930
95679
  }
94931
95680
  const uiSnapshot = loadBridgeUiContextSnapshot({ stateFile: params3.stateFile, staleMs: params3.staleMs });
94932
95681
  const ui = uiSnapshot.ui_context;
94933
- const pageRemId = normalizeText(ui?.pageRemId);
94934
- const focusedRemId = normalizeText(ui?.focusedRemId);
95682
+ const pageRemId = normalizeText3(ui?.pageRemId);
95683
+ const focusedRemId = normalizeText3(ui?.focusedRemId);
94935
95684
  const currentId = ids3[0] ?? "";
94936
95685
  const idsToResolve = uniqueNonEmpty([currentId, pageRemId, focusedRemId]);
94937
95686
  const warnings = [];
94938
- const dbPathCandidate = cfg.remnoteDb || (() => {
94939
- const kbId = normalizeText(ui?.kbId);
94940
- return kbId ? remnoteDbPathForWorkspaceId(kbId) : undefined;
94941
- })();
95687
+ const workspace = yield* resolveWorkspaceSnapshot({
95688
+ stateFile: params3.stateFile,
95689
+ staleMs: params3.staleMs
95690
+ }).pipe(catchAll2(() => succeed8(undefined)));
95691
+ const dbPathCandidate = cfg.remnoteDb ?? (workspace?.resolved ? workspace.dbPath : undefined);
94942
95692
  const titles = yield* gen2(function* () {
94943
95693
  if (!dbPathCandidate || idsToResolve.length === 0)
94944
95694
  return new Map;
@@ -94968,6 +95718,8 @@ function collectSelectionOutlineUseCase(params3) {
94968
95718
  const cfg = yield* AppConfig;
94969
95719
  const snapshot2 = loadBridgeSelectionSnapshot({ stateFile: params3.stateFile, staleMs: params3.staleMs });
94970
95720
  const selection = yield* requireOkRemSelection(snapshot2);
95721
+ const workspace = cfg.remnoteDb ? undefined : yield* requireResolvedWorkspace({ stateFile: params3.stateFile, staleMs: params3.staleMs });
95722
+ const dbPath = cfg.remnoteDb ?? workspace.dbPath;
94971
95723
  const rootIds = selection.remIds.map(String);
94972
95724
  if (rootIds.length === 0) {
94973
95725
  return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "No Rem is currently selected", exitCode: 2, details: snapshot2 }));
@@ -94984,7 +95736,7 @@ function collectSelectionOutlineUseCase(params3) {
94984
95736
  const result = yield* tryPromise2({
94985
95737
  try: async () => await executeOutlineRemSubtree({
94986
95738
  id: rootId,
94987
- dbPath: cfg.remnoteDb,
95739
+ dbPath,
94988
95740
  maxDepth: maxDepthValue,
94989
95741
  startOffset: 0,
94990
95742
  maxNodes: perRootMax,
@@ -94995,7 +95747,14 @@ function collectSelectionOutlineUseCase(params3) {
94995
95747
  maxReferenceDepth: params3.maxReferenceDepth,
94996
95748
  detail: params3.detail === true
94997
95749
  }),
94998
- catch: (e) => cliErrorFromUnknown(e, { code: "DB_UNAVAILABLE", details: { root_id: rootId, db_path: cfg.remnoteDb } })
95750
+ catch: (e) => cliErrorFromUnknown(e, {
95751
+ code: "DB_UNAVAILABLE",
95752
+ details: {
95753
+ root_id: rootId,
95754
+ db_path: dbPath,
95755
+ workspace_id: workspace?.workspaceId
95756
+ }
95757
+ })
94999
95758
  });
95000
95759
  const nodeCount = Number(result.nodeCount ?? 0);
95001
95760
  const n = Number.isFinite(nodeCount) && nodeCount >= 0 ? Math.floor(nodeCount) : 0;
@@ -95032,26 +95791,27 @@ function collectUiContextDescribeUseCase(params3) {
95032
95791
  const selectionSnapshot = loadBridgeSelectionSnapshot({ stateFile: params3.stateFile, staleMs: params3.staleMs });
95033
95792
  const ui = uiSnapshot.ui_context;
95034
95793
  const selection = selectionSnapshot.selection;
95035
- const pageRemId = normalizeText(ui?.pageRemId);
95036
- const focusedPortalId = normalizeText(ui?.focusedPortalId);
95037
- const focusedRemId = normalizeText(ui?.focusedRemId);
95794
+ const pageRemId = normalizeText3(ui?.pageRemId);
95795
+ const focusedPortalId = normalizeText3(ui?.focusedPortalId);
95796
+ const focusedRemId = normalizeText3(ui?.focusedRemId);
95038
95797
  const kind = computePortalKind(pageRemId, focusedPortalId);
95039
95798
  const selectionKind = selection?.kind ?? "none";
95040
95799
  const selectionIds = uniqueNonEmpty(selection?.kind === "rem" ? selection.remIds.map(String) : []);
95041
95800
  const selectionTotalCountRaw = selection?.kind === "rem" ? Number(selection.totalCount ?? 0) : selection?.kind === "text" ? 1 : 0;
95042
95801
  const selectionTotalCount = Number.isFinite(selectionTotalCountRaw) && selectionTotalCountRaw >= 0 ? Math.floor(selectionTotalCountRaw) : 0;
95043
95802
  const selectionTruncated = selection?.kind === "rem" ? selection.truncated === true : false;
95044
- const selectionTextRemId = selection?.kind === "text" ? normalizeText(selection.remId) : "";
95803
+ const selectionTextRemId = selection?.kind === "text" ? normalizeText3(selection.remId) : "";
95045
95804
  const anchorSource = focusedRemId ? "focus" : selection?.kind === "rem" && selectionIds.length > 0 ? "selection" : selection?.kind === "text" && selectionTextRemId ? "selection" : "none";
95046
95805
  const anchorRemId = anchorSource === "focus" ? focusedRemId : anchorSource === "selection" ? selection?.kind === "rem" ? selectionIds[0] ?? "" : selectionTextRemId : "";
95047
95806
  const selectionLimitEffective = clampInt3(params3.selectionLimit ?? 5, 1, 50);
95048
95807
  const selectionShownIds = selectionIds.slice(0, selectionLimitEffective);
95049
95808
  const idsToResolve = uniqueNonEmpty([pageRemId, focusedPortalId, focusedRemId, anchorRemId, ...selectionShownIds]);
95050
95809
  const warnings = [];
95051
- const dbPathCandidate = cfg.remnoteDb || (() => {
95052
- const kbId = normalizeText(ui?.kbId);
95053
- return kbId ? remnoteDbPathForWorkspaceId(kbId) : undefined;
95054
- })();
95810
+ const workspace = yield* resolveWorkspaceSnapshot({
95811
+ stateFile: params3.stateFile,
95812
+ staleMs: params3.staleMs
95813
+ }).pipe(catchAll2(() => succeed8(undefined)));
95814
+ const dbPathCandidate = cfg.remnoteDb ?? (workspace?.resolved ? workspace.dbPath : undefined);
95055
95815
  const titles = yield* gen2(function* () {
95056
95816
  if (!dbPathCandidate || idsToResolve.length === 0)
95057
95817
  return new Map;
@@ -95087,6 +95847,45 @@ function collectUiContextDescribeUseCase(params3) {
95087
95847
  });
95088
95848
  }
95089
95849
 
95850
+ // src/commands/daily/rem-id.ts
95851
+ function optionToUndefined10(opt) {
95852
+ return isSome2(opt) ? opt.value : undefined;
95853
+ }
95854
+ var date6 = text9("date").pipe(optional5, map34(optionToUndefined10));
95855
+ var offsetDays = integer7("offset-days").pipe(optional5, map34(optionToUndefined10));
95856
+ var dailyRemIdCommand = exports_Command.make("rem-id", { date: date6, offsetDays }, ({ date: date7, offsetDays: offsetDays2 }) => gen2(function* () {
95857
+ if (date7 && offsetDays2 !== undefined) {
95858
+ return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "Choose only one of --date or --offset-days", exitCode: 2 }));
95859
+ }
95860
+ const cfg = yield* AppConfig;
95861
+ const hostApi = yield* HostApiClient;
95862
+ if (cfg.apiBaseUrl) {
95863
+ const data2 = yield* hostApi.dailyRemId({
95864
+ baseUrl: cfg.apiBaseUrl,
95865
+ date: date7,
95866
+ offsetDays: offsetDays2
95867
+ });
95868
+ yield* writeSuccess({
95869
+ data: data2,
95870
+ ids: [data2.remId],
95871
+ md: `- ref: ${data2.ref}
95872
+ - rem_id: ${data2.remId}${data2.dateString ? `
95873
+ - date_string: ${data2.dateString}` : ""}
95874
+ `
95875
+ });
95876
+ return;
95877
+ }
95878
+ const data = yield* executeDailyRemIdUseCase({ date: date7, offsetDays: offsetDays2 });
95879
+ yield* writeSuccess({
95880
+ data,
95881
+ ids: [data.remId],
95882
+ md: `- ref: ${data.ref}
95883
+ - rem_id: ${data.remId}${data.dateString ? `
95884
+ - date_string: ${data.dateString}` : ""}
95885
+ `
95886
+ });
95887
+ }).pipe(catchAll2(writeFailure)));
95888
+
95090
95889
  // src/commands/daily/write.ts
95091
95890
  function optionToUndefined11(opt) {
95092
95891
  return isSome2(opt) ? opt.value : undefined;
@@ -95114,7 +95913,7 @@ var bulk = choice5("bulk", ["auto", "always", "never"]).pipe(optional5, map34(op
95114
95913
  var bundleTitle = readOptionalText("bundle-title");
95115
95914
  var BULK_THRESHOLD_LINES = 80;
95116
95915
  var BULK_THRESHOLD_CHARS = 5000;
95117
- function parseDateInput3(raw4) {
95916
+ function parseDateInput2(raw4) {
95118
95917
  const trimmed2 = raw4.trim();
95119
95918
  const match24 = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed2);
95120
95919
  const d = match24 ? new Date(Number(match24[1]), Number(match24[2]) - 1, Number(match24[3])) : new Date(trimmed2);
@@ -95231,7 +96030,7 @@ var dailyWriteCommand = exports_Command.make("write", {
95231
96030
  exitCode: 2
95232
96031
  }));
95233
96032
  }
95234
- const target2 = date8 ? parseDateInput3(date8) : null;
96033
+ const target2 = date8 ? parseDateInput2(date8) : null;
95235
96034
  const createIfMissingBool = noCreateIfMissing2 ? false : createIfMissing2 ? true : undefined;
95236
96035
  const content = markdownValue ?? textValue ?? "";
95237
96036
  const lines3 = content.split(`
@@ -95703,19 +96502,22 @@ var applyCommand = exports_Command.make("apply", {
95703
96502
  timeoutMs: timeoutMs3,
95704
96503
  pollMs: pollMs3
95705
96504
  }
95706
- }) : yield* executeWriteApplyUseCase({
95707
- raw: {
95708
- ...raw4,
95709
- priority: resolvedPriority,
95710
- clientId: resolvedClientId,
95711
- idempotencyKey: resolvedIdempotencyKey,
95712
- meta: metaValue,
95713
- notify: notify3 ?? compiled.notify ?? true,
95714
- ensureDaemon: ensureDaemon4 ?? compiled.ensureDaemon ?? true
95715
- },
95716
- wait: wait3,
95717
- timeoutMs: timeoutMs3,
95718
- pollMs: pollMs3
96505
+ }) : yield* gen2(function* () {
96506
+ yield* validateOptionMutationOps({ scopeLabel: "generic", ops: compiled.ops });
96507
+ return yield* executeWriteApplyUseCase({
96508
+ raw: {
96509
+ ...raw4,
96510
+ priority: resolvedPriority,
96511
+ clientId: resolvedClientId,
96512
+ idempotencyKey: resolvedIdempotencyKey,
96513
+ meta: metaValue,
96514
+ notify: notify3 ?? compiled.notify ?? true,
96515
+ ensureDaemon: ensureDaemon4 ?? compiled.ensureDaemon ?? true
96516
+ },
96517
+ wait: wait3,
96518
+ timeoutMs: timeoutMs3,
96519
+ pollMs: pollMs3
96520
+ });
95719
96521
  });
95720
96522
  const out = compiled.kind === "actions" ? { ...data, alias_map: compiled.aliasMap } : data;
95721
96523
  yield* writeSuccess({
@@ -95730,12 +96532,12 @@ var applyCommand = exports_Command.make("apply", {
95730
96532
  }).pipe(catchAll2(writeFailure)));
95731
96533
 
95732
96534
  // src/services/ApiDaemonFiles.ts
95733
- import { promises as fs18 } from "node:fs";
95734
- import path24 from "node:path";
96535
+ import { promises as fs19 } from "node:fs";
96536
+ import path25 from "node:path";
95735
96537
  class ApiDaemonFiles extends Tag2("ApiDaemonFiles")() {
95736
96538
  }
95737
96539
  function ensureDir9(p3) {
95738
- return fs18.mkdir(path24.dirname(p3), { recursive: true }).then(() => {
96540
+ return fs19.mkdir(path25.dirname(p3), { recursive: true }).then(() => {
95739
96541
  return;
95740
96542
  });
95741
96543
  }
@@ -95743,19 +96545,19 @@ function defaultPidFile2() {
95743
96545
  const envPidFile = process.env.REMNOTE_API_PID_FILE;
95744
96546
  if (typeof envPidFile === "string" && envPidFile.trim())
95745
96547
  return resolveUserFilePath(envPidFile);
95746
- return path24.join(homeDir(), ".agent-remnote", "api.pid");
96548
+ return path25.join(homeDir(), ".agent-remnote", "api.pid");
95747
96549
  }
95748
96550
  function defaultLogFile2() {
95749
96551
  const envLogFile = process.env.REMNOTE_API_LOG_FILE;
95750
96552
  if (typeof envLogFile === "string" && envLogFile.trim())
95751
96553
  return resolveUserFilePath(envLogFile);
95752
- return path24.join(homeDir(), ".agent-remnote", "api.log");
96554
+ return path25.join(homeDir(), ".agent-remnote", "api.log");
95753
96555
  }
95754
96556
  function defaultStateFile2() {
95755
96557
  const envStateFile = process.env.REMNOTE_API_STATE_FILE;
95756
96558
  if (typeof envStateFile === "string" && envStateFile.trim())
95757
96559
  return resolveUserFilePath(envStateFile);
95758
- return path24.join(homeDir(), ".agent-remnote", "api.state.json");
96560
+ return path25.join(homeDir(), ".agent-remnote", "api.state.json");
95759
96561
  }
95760
96562
  function parseJson2(raw4, filePath) {
95761
96563
  try {
@@ -95776,7 +96578,7 @@ var ApiDaemonFilesLive = succeed10(ApiDaemonFiles, {
95776
96578
  readPidFile: (pidFilePath) => tryPromise2({
95777
96579
  try: async () => {
95778
96580
  const resolved = resolveUserFilePath(pidFilePath);
95779
- const raw4 = await fs18.readFile(resolved, "utf8");
96581
+ const raw4 = await fs19.readFile(resolved, "utf8");
95780
96582
  return parseJson2(raw4, resolved);
95781
96583
  },
95782
96584
  catch: (error4) => {
@@ -95801,7 +96603,7 @@ var ApiDaemonFilesLive = succeed10(ApiDaemonFiles, {
95801
96603
  try: async () => {
95802
96604
  const resolved = resolveUserFilePath(pidFilePath);
95803
96605
  await ensureDir9(resolved);
95804
- await fs18.writeFile(resolved, `${JSON.stringify(value8, null, 2)}
96606
+ await fs19.writeFile(resolved, `${JSON.stringify(value8, null, 2)}
95805
96607
  `, "utf8");
95806
96608
  },
95807
96609
  catch: (error4) => new CliError({
@@ -95814,7 +96616,7 @@ var ApiDaemonFilesLive = succeed10(ApiDaemonFiles, {
95814
96616
  deletePidFile: (pidFilePath) => tryPromise2({
95815
96617
  try: async () => {
95816
96618
  const resolved = resolveUserFilePath(pidFilePath);
95817
- await fs18.rm(resolved, { force: true });
96619
+ await fs19.rm(resolved, { force: true });
95818
96620
  },
95819
96621
  catch: (error4) => new CliError({
95820
96622
  code: "INTERNAL",
@@ -95826,7 +96628,7 @@ var ApiDaemonFilesLive = succeed10(ApiDaemonFiles, {
95826
96628
  readStateFile: (stateFilePath) => tryPromise2({
95827
96629
  try: async () => {
95828
96630
  const resolved = resolveUserFilePath(stateFilePath);
95829
- const raw4 = await fs18.readFile(resolved, "utf8");
96631
+ const raw4 = await fs19.readFile(resolved, "utf8");
95830
96632
  return parseJson2(raw4, resolved);
95831
96633
  },
95832
96634
  catch: (error4) => {
@@ -95851,7 +96653,7 @@ var ApiDaemonFilesLive = succeed10(ApiDaemonFiles, {
95851
96653
  try: async () => {
95852
96654
  const resolved = resolveUserFilePath(stateFilePath);
95853
96655
  await ensureDir9(resolved);
95854
- await fs18.writeFile(resolved, `${JSON.stringify(value8, null, 2)}
96656
+ await fs19.writeFile(resolved, `${JSON.stringify(value8, null, 2)}
95855
96657
  `, "utf8");
95856
96658
  },
95857
96659
  catch: (error4) => new CliError({
@@ -95864,7 +96666,7 @@ var ApiDaemonFilesLive = succeed10(ApiDaemonFiles, {
95864
96666
  deleteStateFile: (stateFilePath) => tryPromise2({
95865
96667
  try: async () => {
95866
96668
  const resolved = resolveUserFilePath(stateFilePath);
95867
- await fs18.rm(resolved, { force: true });
96669
+ await fs19.rm(resolved, { force: true });
95868
96670
  },
95869
96671
  catch: (error4) => new CliError({
95870
96672
  code: "INTERNAL",
@@ -95894,6 +96696,7 @@ function childCommandLine2(params3) {
95894
96696
  const args2 = [...execArgv, script, "--daemon-url", params3.wsUrl, "--store-db", params3.storeDb];
95895
96697
  if (params3.remnoteDb)
95896
96698
  args2.push("--remnote-db", params3.remnoteDb);
96699
+ args2.push("--api-base-path", params3.basePath);
95897
96700
  args2.push("api", "serve", "--host", params3.host, "--port", String(params3.port), "--state-file", params3.stateFile);
95898
96701
  return { command, args: args2 };
95899
96702
  }
@@ -95947,10 +96750,11 @@ function startApiDaemon(params3) {
95947
96750
  const proc = yield* Process;
95948
96751
  const host = params3.host ?? cfg.apiHost ?? "0.0.0.0";
95949
96752
  const port3 = params3.port ?? cfg.apiPort ?? 3000;
96753
+ const basePath2 = cfg.apiBasePath ?? "/v1";
95950
96754
  const pidFilePath = resolveUserFilePath(params3.pidFile ?? apiFiles.defaultPidFile());
95951
96755
  const logFilePath = resolveUserFilePath(params3.logFile ?? apiFiles.defaultLogFile());
95952
96756
  const stateFilePath = resolveUserFilePath(params3.stateFile ?? apiFiles.defaultStateFile());
95953
- const localBaseUrl = apiLocalBaseUrl(port3);
96757
+ const localBaseUrl = apiLocalBaseUrl(port3, basePath2);
95954
96758
  const existing = yield* apiFiles.readPidFile(pidFilePath);
95955
96759
  if (existing) {
95956
96760
  const alive = yield* proc.isPidRunning(existing.pid);
@@ -95984,6 +96788,7 @@ function startApiDaemon(params3) {
95984
96788
  remnoteDb: cfg.remnoteDb,
95985
96789
  host,
95986
96790
  port: port3,
96791
+ basePath: basePath2,
95987
96792
  stateFile: stateFilePath
95988
96793
  }),
95989
96794
  catch: (e) => isCliError(e) ? e : new CliError({
@@ -95999,7 +96804,7 @@ function startApiDaemon(params3) {
95999
96804
  startedAt: Date.now(),
96000
96805
  host,
96001
96806
  port: port3,
96002
- basePath: "/v1",
96807
+ basePath: basePath2,
96003
96808
  logFile: logFilePath,
96004
96809
  stateFile: stateFilePath,
96005
96810
  cmd: [cmd.command, ...cmd.args]
@@ -96022,10 +96827,11 @@ function ensureApiDaemon(params3) {
96022
96827
  const apiFiles = yield* ApiDaemonFiles;
96023
96828
  const proc = yield* Process;
96024
96829
  const port3 = params3.port ?? cfg.apiPort ?? 3000;
96830
+ const basePath2 = cfg.apiBasePath ?? "/v1";
96025
96831
  const pidFilePath = resolveUserFilePath(params3.pidFile ?? apiFiles.defaultPidFile());
96026
96832
  const logFilePath = resolveUserFilePath(params3.logFile ?? apiFiles.defaultLogFile());
96027
96833
  const stateFilePath = resolveUserFilePath(params3.stateFile ?? apiFiles.defaultStateFile());
96028
- const localBaseUrl = apiLocalBaseUrl(port3);
96834
+ const localBaseUrl = apiLocalBaseUrl(port3, basePath2);
96029
96835
  const pre = yield* api.health({ baseUrl: localBaseUrl, timeoutMs: API_HEALTH_TIMEOUT_MS }).pipe(either3);
96030
96836
  if (isRight2(pre)) {
96031
96837
  const existing = yield* apiFiles.readPidFile(pidFilePath);
@@ -96198,9 +97004,13 @@ function statusCodeFromCliError(error4) {
96198
97004
  case "INVALID_PAYLOAD":
96199
97005
  case "PAYLOAD_TOO_LARGE":
96200
97006
  return 400;
97007
+ case "WORKSPACE_UNRESOLVED":
96201
97008
  case "TXN_FAILED":
96202
97009
  case "ID_MAP_CONFLICT":
96203
97010
  return 409;
97011
+ case "PLUGIN_UNAVAILABLE":
97012
+ case "WRITE_UNAVAILABLE":
97013
+ case "UI_SESSION_UNAVAILABLE":
96204
97014
  case "WS_UNAVAILABLE":
96205
97015
  case "WS_TIMEOUT":
96206
97016
  case "DB_UNAVAILABLE":
@@ -96262,6 +97072,7 @@ function runHttpApiRuntime(params3) {
96262
97072
  return gen2(function* () {
96263
97073
  const cfg = yield* AppConfig;
96264
97074
  const runtimeCfg = { ...cfg, apiBaseUrl: undefined };
97075
+ const basePath2 = normalizeApiBasePath2(cfg.apiBasePath ?? "/v1");
96265
97076
  const apiFiles = yield* ApiDaemonFiles;
96266
97077
  const daemonFiles = yield* DaemonFiles;
96267
97078
  const ws = yield* WsClient;
@@ -96269,6 +97080,7 @@ function runHttpApiRuntime(params3) {
96269
97080
  const payload = yield* Payload;
96270
97081
  const refs = yield* RefResolver;
96271
97082
  const hostApi = yield* HostApiClient;
97083
+ const workspaceBindings = yield* WorkspaceBindings;
96272
97084
  const remDb = yield* RemDb;
96273
97085
  const processSvc = yield* Process;
96274
97086
  const supervisorState = yield* SupervisorState;
@@ -96277,10 +97089,20 @@ function runHttpApiRuntime(params3) {
96277
97089
  const configuredPort = params3?.port ?? cfg.apiPort ?? 3000;
96278
97090
  const stateFilePath = params3?.stateFile ?? cfg.apiStateFile ?? apiFiles.defaultStateFile();
96279
97091
  const startedAt = Date.now();
96280
- const provide6 = (effect4) => effect4.pipe(provideService2(AppConfig, runtimeCfg), provideService2(ApiDaemonFiles, apiFiles), provideService2(DaemonFiles, daemonFiles), provideService2(WsClient, ws), provideService2(Queue, queue), provideService2(Payload, payload), provideService2(RefResolver, refs), provideService2(HostApiClient, hostApi), provideService2(RemDb, remDb), provideService2(Process, processSvc), provideService2(SupervisorState, supervisorState), provideService2(StatusLineController, statusLine));
97092
+ const provide6 = (effect4) => effect4.pipe(provideService2(AppConfig, runtimeCfg), provideService2(ApiDaemonFiles, apiFiles), provideService2(DaemonFiles, daemonFiles), provideService2(WsClient, ws), provideService2(Queue, queue), provideService2(Payload, payload), provideService2(RefResolver, refs), provideService2(HostApiClient, hostApi), provideService2(WorkspaceBindings, workspaceBindings), provideService2(RemDb, remDb), provideService2(Process, processSvc), provideService2(SupervisorState, supervisorState), provideService2(StatusLineController, statusLine));
97093
+ const routePathFor = (pathname) => {
97094
+ if (basePath2 === "/")
97095
+ return pathname || "/";
97096
+ if (pathname === basePath2)
97097
+ return "/";
97098
+ if (pathname.startsWith(`${basePath2}/`))
97099
+ return pathname.slice(basePath2.length) || "/";
97100
+ return null;
97101
+ };
96281
97102
  const server = createServer((req, res) => {
96282
97103
  const url2 = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
96283
97104
  const method = req.method || "GET";
97105
+ const routePath = routePathFor(url2.pathname);
96284
97106
  const run12 = async (effect4, statusCode = 200) => {
96285
97107
  const exit3 = await runPromiseExit(provide6(effect4));
96286
97108
  if (isSuccess(exit3)) {
@@ -96295,60 +97117,60 @@ function runHttpApiRuntime(params3) {
96295
97117
  });
96296
97118
  sendJson2(res, statusCodeFromCliError(cliError), fail21(toJsonError(cliError), cliError.hint));
96297
97119
  };
96298
- if (method === "GET" && url2.pathname === "/v1/health") {
97120
+ if (method === "GET" && routePath === "/health") {
96299
97121
  run12(gen2(function* () {
96300
97122
  return yield* collectApiHealthUseCase({
96301
97123
  pid: process.pid,
96302
97124
  host: host3,
96303
97125
  port: currentPort(),
96304
- basePath: "/v1",
97126
+ basePath: basePath2,
96305
97127
  startedAt
96306
97128
  });
96307
97129
  }));
96308
97130
  return;
96309
97131
  }
96310
- if (method === "GET" && url2.pathname === "/v1/status") {
97132
+ if (method === "GET" && routePath === "/status") {
96311
97133
  run12(gen2(function* () {
96312
97134
  return yield* collectApiStatusUseCase({
96313
97135
  pid: process.pid,
96314
97136
  host: host3,
96315
97137
  port: currentPort(),
96316
- basePath: "/v1",
97138
+ basePath: basePath2,
96317
97139
  startedAt
96318
97140
  });
96319
97141
  }));
96320
97142
  return;
96321
97143
  }
96322
- if (method === "GET" && url2.pathname === "/v1/ui-context") {
97144
+ if (method === "GET" && routePath === "/ui-context") {
96323
97145
  sendJson2(res, 200, ok(loadBridgeUiContextSnapshot({})));
96324
97146
  return;
96325
97147
  }
96326
- if (method === "GET" && url2.pathname === "/v1/selection") {
97148
+ if (method === "GET" && routePath === "/selection") {
96327
97149
  sendJson2(res, 200, ok(loadBridgeSelectionSnapshot({})));
96328
97150
  return;
96329
97151
  }
96330
- if (method === "GET" && url2.pathname === "/v1/plugin/ui-context/snapshot") {
97152
+ if (method === "GET" && routePath === "/plugin/ui-context/snapshot") {
96331
97153
  run12(collectUiContextSnapshotUseCase({
96332
97154
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96333
97155
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined
96334
97156
  }));
96335
97157
  return;
96336
97158
  }
96337
- if (method === "GET" && url2.pathname === "/v1/plugin/ui-context/page") {
97159
+ if (method === "GET" && routePath === "/plugin/ui-context/page") {
96338
97160
  run12(collectUiContextPageUseCase({
96339
97161
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96340
97162
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined
96341
97163
  }));
96342
97164
  return;
96343
97165
  }
96344
- if (method === "GET" && url2.pathname === "/v1/plugin/ui-context/focused-rem") {
97166
+ if (method === "GET" && routePath === "/plugin/ui-context/focused-rem") {
96345
97167
  run12(collectUiContextFocusedRemUseCase({
96346
97168
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96347
97169
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined
96348
97170
  }));
96349
97171
  return;
96350
97172
  }
96351
- if (method === "GET" && url2.pathname === "/v1/plugin/ui-context/describe") {
97173
+ if (method === "GET" && routePath === "/plugin/ui-context/describe") {
96352
97174
  run12(collectUiContextDescribeUseCase({
96353
97175
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96354
97176
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined,
@@ -96356,28 +97178,28 @@ function runHttpApiRuntime(params3) {
96356
97178
  }));
96357
97179
  return;
96358
97180
  }
96359
- if (method === "GET" && url2.pathname === "/v1/plugin/selection/snapshot") {
97181
+ if (method === "GET" && routePath === "/plugin/selection/snapshot") {
96360
97182
  run12(collectSelectionSnapshotUseCase({
96361
97183
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96362
97184
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined
96363
97185
  }));
96364
97186
  return;
96365
97187
  }
96366
- if (method === "GET" && url2.pathname === "/v1/plugin/selection/roots") {
97188
+ if (method === "GET" && routePath === "/plugin/selection/roots") {
96367
97189
  run12(collectSelectionRootsUseCase({
96368
97190
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96369
97191
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined
96370
97192
  }));
96371
97193
  return;
96372
97194
  }
96373
- if (method === "GET" && url2.pathname === "/v1/plugin/selection/current") {
97195
+ if (method === "GET" && routePath === "/plugin/selection/current") {
96374
97196
  run12(collectSelectionCurrentUseCase({
96375
97197
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96376
97198
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined
96377
97199
  }));
96378
97200
  return;
96379
97201
  }
96380
- if (method === "GET" && url2.pathname === "/v1/plugin/current") {
97202
+ if (method === "GET" && routePath === "/plugin/current") {
96381
97203
  run12(collectPluginCurrentUseCase({
96382
97204
  stateFile: url2.searchParams.get("stateFile") ?? undefined,
96383
97205
  staleMs: url2.searchParams.get("staleMs") ? Number(url2.searchParams.get("staleMs")) : undefined,
@@ -96385,14 +97207,14 @@ function runHttpApiRuntime(params3) {
96385
97207
  }));
96386
97208
  return;
96387
97209
  }
96388
- if (method === "GET" && url2.pathname === "/v1/daily/rem-id") {
97210
+ if (method === "GET" && routePath === "/daily/rem-id") {
96389
97211
  run12(executeDailyRemIdUseCase({
96390
97212
  date: url2.searchParams.get("date") ?? undefined,
96391
97213
  offsetDays: url2.searchParams.get("offsetDays") ? Number(url2.searchParams.get("offsetDays")) : undefined
96392
97214
  }));
96393
97215
  return;
96394
97216
  }
96395
- if (method === "POST" && url2.pathname === "/v1/plugin/selection/outline") {
97217
+ if (method === "POST" && routePath === "/plugin/selection/outline") {
96396
97218
  (async () => {
96397
97219
  try {
96398
97220
  const body = await runPromise(readJsonBody(req));
@@ -96419,7 +97241,7 @@ function runHttpApiRuntime(params3) {
96419
97241
  })();
96420
97242
  return;
96421
97243
  }
96422
- if (method === "POST" && url2.pathname === "/v1/search/db") {
97244
+ if (method === "POST" && routePath === "/search/db") {
96423
97245
  (async () => {
96424
97246
  try {
96425
97247
  const body = await runPromise(readJsonBody(req));
@@ -96445,7 +97267,7 @@ function runHttpApiRuntime(params3) {
96445
97267
  })();
96446
97268
  return;
96447
97269
  }
96448
- if (method === "POST" && url2.pathname === "/v1/read/outline") {
97270
+ if (method === "POST" && routePath === "/read/outline") {
96449
97271
  (async () => {
96450
97272
  try {
96451
97273
  const body = await runPromise(readJsonBody(req));
@@ -96474,7 +97296,7 @@ function runHttpApiRuntime(params3) {
96474
97296
  })();
96475
97297
  return;
96476
97298
  }
96477
- if (method === "POST" && url2.pathname === "/v1/search/plugin") {
97299
+ if (method === "POST" && routePath === "/search/plugin") {
96478
97300
  (async () => {
96479
97301
  try {
96480
97302
  const body = await runPromise(readJsonBody(req));
@@ -96497,7 +97319,7 @@ function runHttpApiRuntime(params3) {
96497
97319
  })();
96498
97320
  return;
96499
97321
  }
96500
- if (method === "POST" && url2.pathname === "/v1/write/apply") {
97322
+ if (method === "POST" && routePath === "/write/apply") {
96501
97323
  (async () => {
96502
97324
  try {
96503
97325
  const body = await runPromise(readJsonBody(req));
@@ -96525,7 +97347,7 @@ function runHttpApiRuntime(params3) {
96525
97347
  })();
96526
97348
  return;
96527
97349
  }
96528
- if (method === "POST" && url2.pathname === "/v1/queue/wait") {
97350
+ if (method === "POST" && routePath === "/queue/wait") {
96529
97351
  (async () => {
96530
97352
  try {
96531
97353
  const body = await runPromise(readJsonBody(req));
@@ -96542,12 +97364,12 @@ function runHttpApiRuntime(params3) {
96542
97364
  })();
96543
97365
  return;
96544
97366
  }
96545
- if (method === "GET" && url2.pathname.startsWith("/v1/queue/txns/")) {
96546
- const txnId = decodeURIComponent(url2.pathname.slice("/v1/queue/txns/".length));
97367
+ if (method === "GET" && typeof routePath === "string" && routePath.startsWith("/queue/txns/")) {
97368
+ const txnId = decodeURIComponent(routePath.slice("/queue/txns/".length));
96547
97369
  run12(executeQueueTxnUseCase({ txnId }));
96548
97370
  return;
96549
97371
  }
96550
- if (method === "POST" && url2.pathname === "/v1/actions/trigger-sync") {
97372
+ if (method === "POST" && routePath === "/actions/trigger-sync") {
96551
97373
  run12(executeTriggerSyncUseCase());
96552
97374
  return;
96553
97375
  }
@@ -96590,10 +97412,10 @@ function runHttpApiRuntime(params3) {
96590
97412
  pid: process.pid,
96591
97413
  host: host3,
96592
97414
  port: actualPort,
96593
- basePath: "/v1",
97415
+ basePath: basePath2,
96594
97416
  startedAt,
96595
- localBaseUrl: apiLocalBaseUrl(actualPort),
96596
- containerBaseUrl: apiContainerBaseUrl(actualPort),
97417
+ localBaseUrl: apiLocalBaseUrl(actualPort, basePath2),
97418
+ containerBaseUrl: apiContainerBaseUrl(actualPort, basePath2),
96597
97419
  daemon: { healthy: daemonHealth._tag === "Right", wsUrl: cfg.wsUrl }
96598
97420
  });
96599
97421
  const waitForStop = async((resume2) => {
@@ -96680,7 +97502,8 @@ var apiStatusCommand = exports_Command.make("status", { pidFile: pidFile12, stat
96680
97502
  const pid = pidInfo?.pid;
96681
97503
  const running4 = typeof pid === "number" ? yield* proc.isPidRunning(pid) : false;
96682
97504
  const port7 = pidInfo?.port ?? state?.port ?? cfg.apiPort ?? 3000;
96683
- const localBaseUrl = apiLocalBaseUrl(port7);
97505
+ const basePath2 = pidInfo?.base_path ?? state?.basePath ?? cfg.apiBasePath ?? "/v1";
97506
+ const localBaseUrl = apiLocalBaseUrl(port7, basePath2);
96684
97507
  const health = yield* api.health({ baseUrl: localBaseUrl, timeoutMs: API_HEALTH_TIMEOUT_MS }).pipe(either3);
96685
97508
  const status2 = yield* api.status({ baseUrl: localBaseUrl, timeoutMs: API_HEALTH_TIMEOUT_MS }).pipe(either3);
96686
97509
  const data = {
@@ -96696,6 +97519,7 @@ var apiStatusCommand = exports_Command.make("status", { pidFile: pidFile12, stat
96696
97519
  api: {
96697
97520
  healthy: health._tag === "Right",
96698
97521
  base_url: localBaseUrl,
97522
+ base_path: basePath2,
96699
97523
  status: status2._tag === "Right" ? status2.right : null,
96700
97524
  error: health._tag === "Left" ? health.left.message : undefined
96701
97525
  }
@@ -96708,7 +97532,16 @@ var apiStatusCommand = exports_Command.make("status", { pidFile: pidFile12, stat
96708
97532
  `- state_file: ${data.service.state_file}`,
96709
97533
  `- started_at: ${data.service.started_at ?? ""}`,
96710
97534
  `- api_healthy: ${data.api.healthy}`,
96711
- `- base_url: ${data.api.base_url}`
97535
+ `- base_url: ${data.api.base_url}`,
97536
+ `- base_path: ${data.api.base_path}`,
97537
+ `- db_read_ready: ${data.api.status?.capabilities?.db_read_ready ?? ""}`,
97538
+ `- plugin_rpc_ready: ${data.api.status?.capabilities?.plugin_rpc_ready ?? ""}`,
97539
+ `- write_ready: ${data.api.status?.capabilities?.write_ready ?? ""}`,
97540
+ `- ui_session_ready: ${data.api.status?.capabilities?.ui_session_ready ?? ""}`,
97541
+ `- workspace_resolved: ${data.api.status?.workspace?.resolved ?? ""}`,
97542
+ `- current_workspace_id: ${data.api.status?.workspace?.currentWorkspaceId ?? ""}`,
97543
+ `- current_db_path: ${data.api.status?.workspace?.currentDbPath ?? ""}`,
97544
+ `- binding_source: ${data.api.status?.workspace?.bindingSource ?? ""}`
96712
97545
  ].join(`
96713
97546
  `);
96714
97547
  yield* writeSuccess({ data, md });
@@ -97101,7 +97934,7 @@ function optionToUndefined26(opt) {
97101
97934
  var stateFile13 = text9("state-file").pipe(optional5, map34(optionToUndefined26));
97102
97935
  var staleMs6 = integer7("stale-ms").pipe(optional5, map34(optionToUndefined26));
97103
97936
  var selectionLimit = integer7("selection-limit").pipe(withDefault5(5));
97104
- function normalizeText2(value8) {
97937
+ function normalizeText4(value8) {
97105
97938
  return typeof value8 === "string" ? value8.trim() : "";
97106
97939
  }
97107
97940
  function clampInt5(value8, min5, max7) {
@@ -97118,8 +97951,8 @@ function truncateText2(value8, maxLength) {
97118
97951
  return `${normalized.slice(0, maxLength)}…`;
97119
97952
  }
97120
97953
  function pickTitle2(kt, ke, r) {
97121
- const combined = [kt, ke].map(normalizeText2).filter(Boolean).join(" | ");
97122
- const raw4 = combined || normalizeText2(r);
97954
+ const combined = [kt, ke].map(normalizeText4).filter(Boolean).join(" | ");
97955
+ const raw4 = combined || normalizeText4(r);
97123
97956
  if (!raw4)
97124
97957
  return "";
97125
97958
  const normalized = raw4.replace(/\s+/g, " ").trim();
@@ -97203,16 +98036,16 @@ var readUiContextDescribeCommand = exports_Command.make("describe", { stateFile:
97203
98036
  const selectionSnapshot = loadBridgeSelectionSnapshot({ stateFile: stateFile14, staleMs: staleMs7 });
97204
98037
  const ui = uiSnapshot.ui_context;
97205
98038
  const selection = selectionSnapshot.selection;
97206
- const pageRemId = normalizeText2(ui?.pageRemId);
97207
- const focusedPortalId = normalizeText2(ui?.focusedPortalId);
97208
- const focusedRemId = normalizeText2(ui?.focusedRemId);
98039
+ const pageRemId = normalizeText4(ui?.pageRemId);
98040
+ const focusedPortalId = normalizeText4(ui?.focusedPortalId);
98041
+ const focusedRemId = normalizeText4(ui?.focusedRemId);
97209
98042
  const kind = computePortalKind2(pageRemId, focusedPortalId);
97210
98043
  const selectionKind = selection?.kind ?? "none";
97211
98044
  const selectionIds = uniqueNonEmpty2(selection?.kind === "rem" ? selection.remIds.map(String) : []);
97212
98045
  const selectionTotalCountRaw = selection?.kind === "rem" ? Number(selection.totalCount ?? 0) : selection?.kind === "text" ? 1 : 0;
97213
98046
  const selectionTotalCount = Number.isFinite(selectionTotalCountRaw) && selectionTotalCountRaw >= 0 ? Math.floor(selectionTotalCountRaw) : 0;
97214
98047
  const selectionTruncated = selection?.kind === "rem" ? selection.truncated === true : false;
97215
- const selectionTextRemId = selection?.kind === "text" ? normalizeText2(selection.remId) : "";
98048
+ const selectionTextRemId = selection?.kind === "text" ? normalizeText4(selection.remId) : "";
97216
98049
  const selectionTextRange = selection?.kind === "text" ? {
97217
98050
  start: Number.isFinite(selection.range?.start) ? Math.floor(selection.range.start) : NaN,
97218
98051
  end: Number.isFinite(selection.range?.end) ? Math.floor(selection.range.end) : NaN,
@@ -97230,10 +98063,8 @@ var readUiContextDescribeCommand = exports_Command.make("describe", { stateFile:
97230
98063
  ...selectionShownIds
97231
98064
  ]);
97232
98065
  const warnings = [];
97233
- const dbPathCandidate = cfg.remnoteDb || (() => {
97234
- const kbId = normalizeText2(ui?.kbId);
97235
- return kbId ? remnoteDbPathForWorkspaceId(kbId) : undefined;
97236
- })();
98066
+ const workspace = yield* resolveWorkspaceSnapshot({ stateFile: stateFile14, staleMs: staleMs7 }).pipe(catchAll2(() => succeed8(undefined)));
98067
+ const dbPathCandidate = cfg.remnoteDb ?? (workspace?.resolved ? workspace.dbPath : undefined);
97237
98068
  const titles = yield* gen2(function* () {
97238
98069
  if (!dbPathCandidate || idsToResolve.length === 0)
97239
98070
  return new Map;
@@ -98481,6 +99312,8 @@ var writePowerupOptionAddCommand = exports_Command.make("add", {
98481
99312
  exitCode: 2
98482
99313
  }));
98483
99314
  }
99315
+ const cfg = yield* AppConfig;
99316
+ const hostApi = yield* HostApiClient;
98484
99317
  const payloadSvc = yield* Payload;
98485
99318
  const op = yield* try_3({
98486
99319
  try: () => normalizeOp({ type: "add_option", payload: { propertyId: property, text: text15 } }, payloadSvc.normalizeKeys),
@@ -98492,6 +99325,53 @@ var writePowerupOptionAddCommand = exports_Command.make("add", {
98492
99325
  })
98493
99326
  });
98494
99327
  const metaValue = meta ? yield* payloadSvc.readJson(meta) : undefined;
99328
+ if (cfg.apiBaseUrl) {
99329
+ if (dryRun) {
99330
+ yield* writeSuccess({
99331
+ data: { dry_run: true, ops: [op], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
99332
+ md: `- dry_run: true
99333
+ - op: add_option
99334
+ - property_id: ${property}
99335
+ `
99336
+ });
99337
+ return;
99338
+ }
99339
+ const data2 = yield* hostApi.writeApply({
99340
+ baseUrl: cfg.apiBaseUrl,
99341
+ body: {
99342
+ version: 1,
99343
+ kind: "ops",
99344
+ ops: [op],
99345
+ priority: priority3,
99346
+ clientId: clientId3,
99347
+ idempotencyKey: idempotencyKey3,
99348
+ meta: metaValue,
99349
+ notify: notify3,
99350
+ ensureDaemon: ensureDaemon5
99351
+ }
99352
+ });
99353
+ const waited2 = wait3 ? yield* hostApi.queueWait({
99354
+ baseUrl: cfg.apiBaseUrl,
99355
+ txnId: String(data2.txn_id),
99356
+ timeoutMs: timeoutMs4,
99357
+ pollMs: pollMs3
99358
+ }) : null;
99359
+ const out2 = waited2 ? { ...data2, ...waited2 } : data2;
99360
+ yield* writeSuccess({
99361
+ data: out2,
99362
+ ids: [data2.txn_id, ...data2.op_ids],
99363
+ md: [
99364
+ `- txn_id: ${data2.txn_id}`,
99365
+ `- op_ids: ${data2.op_ids.length}`,
99366
+ `- notified: ${data2.notified}`,
99367
+ `- sent: ${data2.sent ?? ""}`,
99368
+ ...waited2 ? [`- status: ${waited2.status}`, `- elapsed_ms: ${waited2.elapsed_ms}`] : []
99369
+ ].join(`
99370
+ `)
99371
+ });
99372
+ return;
99373
+ }
99374
+ yield* ensureOptionMutationSupportedForProperty({ scopeLabel: "powerup", propertyId: property });
98495
99375
  if (dryRun) {
98496
99376
  yield* writeSuccess({
98497
99377
  data: { dry_run: true, ops: [op], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
@@ -98555,17 +99435,66 @@ var writePowerupOptionRemoveCommand = exports_Command.make("remove", {
98555
99435
  exitCode: 2
98556
99436
  }));
98557
99437
  }
98558
- const payloadSvc = yield* Payload;
98559
- const op = yield* try_3({
98560
- try: () => normalizeOp({ type: "remove_option", payload: { optionId: option6 } }, payloadSvc.normalizeKeys),
98561
- catch: (e) => isCliError(e) ? e : new CliError({
98562
- code: "INVALID_PAYLOAD",
98563
- message: "Failed to generate op",
98564
- exitCode: 2,
98565
- details: { error: String(e?.message || e) }
98566
- })
98567
- });
98568
- const metaValue = meta ? yield* payloadSvc.readJson(meta) : undefined;
99438
+ const cfg = yield* AppConfig;
99439
+ const hostApi = yield* HostApiClient;
99440
+ const payloadSvc = yield* Payload;
99441
+ const op = yield* try_3({
99442
+ try: () => normalizeOp({ type: "remove_option", payload: { optionId: option6 } }, payloadSvc.normalizeKeys),
99443
+ catch: (e) => isCliError(e) ? e : new CliError({
99444
+ code: "INVALID_PAYLOAD",
99445
+ message: "Failed to generate op",
99446
+ exitCode: 2,
99447
+ details: { error: String(e?.message || e) }
99448
+ })
99449
+ });
99450
+ const metaValue = meta ? yield* payloadSvc.readJson(meta) : undefined;
99451
+ if (cfg.apiBaseUrl) {
99452
+ if (dryRun) {
99453
+ yield* writeSuccess({
99454
+ data: { dry_run: true, ops: [op], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
99455
+ md: `- dry_run: true
99456
+ - op: remove_option
99457
+ - option_id: ${option6}
99458
+ `
99459
+ });
99460
+ return;
99461
+ }
99462
+ const data2 = yield* hostApi.writeApply({
99463
+ baseUrl: cfg.apiBaseUrl,
99464
+ body: {
99465
+ version: 1,
99466
+ kind: "ops",
99467
+ ops: [op],
99468
+ priority: priority3,
99469
+ clientId: clientId3,
99470
+ idempotencyKey: idempotencyKey3,
99471
+ meta: metaValue,
99472
+ notify: notify3,
99473
+ ensureDaemon: ensureDaemon5
99474
+ }
99475
+ });
99476
+ const waited2 = wait3 ? yield* hostApi.queueWait({
99477
+ baseUrl: cfg.apiBaseUrl,
99478
+ txnId: String(data2.txn_id),
99479
+ timeoutMs: timeoutMs4,
99480
+ pollMs: pollMs3
99481
+ }) : null;
99482
+ const out2 = waited2 ? { ...data2, ...waited2 } : data2;
99483
+ yield* writeSuccess({
99484
+ data: out2,
99485
+ ids: [data2.txn_id, ...data2.op_ids],
99486
+ md: [
99487
+ `- txn_id: ${data2.txn_id}`,
99488
+ `- op_ids: ${data2.op_ids.length}`,
99489
+ `- notified: ${data2.notified}`,
99490
+ `- sent: ${data2.sent ?? ""}`,
99491
+ ...waited2 ? [`- status: ${waited2.status}`, `- elapsed_ms: ${waited2.elapsed_ms}`] : []
99492
+ ].join(`
99493
+ `)
99494
+ });
99495
+ return;
99496
+ }
99497
+ yield* ensureOptionMutationSupportedForOption({ scopeLabel: "powerup", optionId: option6 });
98569
99498
  if (dryRun) {
98570
99499
  yield* writeSuccess({
98571
99500
  data: { dry_run: true, ops: [op], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
@@ -98666,6 +99595,11 @@ var writePowerupPropertyAddCommand = exports_Command.make("add", {
98666
99595
  exitCode: 2
98667
99596
  }));
98668
99597
  }
99598
+ yield* ensureTypedPropertyCreationSupported({
99599
+ scopeLabel: "powerup",
99600
+ type: type2,
99601
+ hasOptions: typeof options6 === "string" && options6.trim().length > 0
99602
+ });
98669
99603
  const payloadSvc = yield* Payload;
98670
99604
  const resolved = powerup2 ? yield* resolvePowerup(powerup2) : null;
98671
99605
  const resolvedTagId = resolved ? resolved.id : normalizeRemIdInput(tagId);
@@ -98761,78 +99695,8 @@ var writePowerupPropertySetTypeCommand = exports_Command.make("set-type", {
98761
99695
  clientId: writeCommonOptions.clientId,
98762
99696
  idempotencyKey: writeCommonOptions.idempotencyKey,
98763
99697
  meta: writeCommonOptions.meta
98764
- }, ({
98765
- property,
98766
- type: type2,
98767
- notify: notify3,
98768
- ensureDaemon: ensureDaemon5,
98769
- wait: wait3,
98770
- timeoutMs: timeoutMs4,
98771
- pollMs: pollMs3,
98772
- dryRun,
98773
- priority: priority3,
98774
- clientId: clientId3,
98775
- idempotencyKey: idempotencyKey3,
98776
- meta
98777
- }) => gen2(function* () {
98778
- if (!wait3 && (timeoutMs4 !== undefined || pollMs3 !== undefined)) {
98779
- return yield* fail8(new CliError({
98780
- code: "INVALID_ARGS",
98781
- message: "Use --wait to enable --timeout-ms/--poll-ms",
98782
- exitCode: 2
98783
- }));
98784
- }
98785
- if (dryRun && wait3) {
98786
- return yield* fail8(new CliError({
98787
- code: "INVALID_ARGS",
98788
- message: "--wait is not compatible with --dry-run",
98789
- exitCode: 2
98790
- }));
98791
- }
98792
- const payloadSvc = yield* Payload;
98793
- const op = yield* try_3({
98794
- try: () => normalizeOp({ type: "set_property_type", payload: { propertyId: property, type: type2 } }, payloadSvc.normalizeKeys),
98795
- catch: (e) => isCliError(e) ? e : new CliError({
98796
- code: "INVALID_PAYLOAD",
98797
- message: "Failed to generate op",
98798
- exitCode: 2,
98799
- details: { error: String(e?.message || e) }
98800
- })
98801
- });
98802
- const metaValue = meta ? yield* payloadSvc.readJson(meta) : undefined;
98803
- if (dryRun) {
98804
- yield* writeSuccess({
98805
- data: { dry_run: true, ops: [op], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
98806
- md: `- dry_run: true
98807
- - op: set_property_type
98808
- - property_id: ${property}
98809
- `
98810
- });
98811
- return;
98812
- }
98813
- const data = yield* enqueueOps({
98814
- ops: [op],
98815
- priority: priority3,
98816
- clientId: clientId3,
98817
- idempotencyKey: idempotencyKey3,
98818
- meta: metaValue,
98819
- notify: notify3,
98820
- ensureDaemon: ensureDaemon5
98821
- });
98822
- const waited = wait3 ? yield* waitForTxn({ txnId: data.txn_id, timeoutMs: timeoutMs4, pollMs: pollMs3 }) : null;
98823
- const out = waited ? { ...data, ...waited } : data;
98824
- yield* writeSuccess({
98825
- data: out,
98826
- ids: [data.txn_id, ...data.op_ids],
98827
- md: [
98828
- `- txn_id: ${data.txn_id}`,
98829
- `- op_ids: ${data.op_ids.length}`,
98830
- `- notified: ${data.notified}`,
98831
- `- sent: ${data.sent ?? ""}`,
98832
- ...waited ? [`- status: ${waited.status}`, `- elapsed_ms: ${waited.elapsed_ms}`] : []
98833
- ].join(`
98834
- `)
98835
- });
99698
+ }, () => gen2(function* () {
99699
+ return yield* failUnsupportedPropertyTypeMutation("powerup");
98836
99700
  }).pipe(catchAll2(writeFailure)));
98837
99701
 
98838
99702
  // src/commands/write/powerup/property/index.ts
@@ -100015,7 +100879,6 @@ var readOutlineCommand = exports_Command.make("outline", {
100015
100879
  }) => gen2(function* () {
100016
100880
  const cfg = yield* AppConfig;
100017
100881
  const hostApi = yield* HostApiClient;
100018
- const refs = yield* RefResolver;
100019
100882
  if (id4 && ref2) {
100020
100883
  return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "Choose only one of --id or --ref", exitCode: 2 }));
100021
100884
  }
@@ -100042,28 +100905,18 @@ var readOutlineCommand = exports_Command.make("outline", {
100042
100905
  yield* writeSuccess({ data, md: data.markdown ?? "" });
100043
100906
  return;
100044
100907
  }
100045
- const link3 = ref2 ? tryParseRemnoteLinkFromRef(ref2) : undefined;
100046
- const resolvedId = ref2 ? link3?.remId ?? (yield* refs.resolve(ref2)) : id4;
100047
- if (!resolvedId) {
100048
- return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "You must provide --id or --ref", exitCode: 2 }));
100049
- }
100050
- const inferredDbPath = link3?.workspaceId ? tryResolveRemnoteDbPathForWorkspaceIdSync(link3.workspaceId) : undefined;
100051
- const dbPath = cfg.remnoteDb ?? inferredDbPath;
100052
- const result = yield* tryPromise2({
100053
- try: async () => await executeOutlineRemSubtree({
100054
- id: resolvedId,
100055
- dbPath,
100056
- maxDepth: depth2,
100057
- startOffset: offset6,
100058
- maxNodes: nodes2,
100059
- format: format10 === "json" ? "json" : "markdown",
100060
- excludeProperties,
100061
- includeEmpty,
100062
- expandReferences: expandReferences === false ? false : undefined,
100063
- maxReferenceDepth: maxReferenceDepth2,
100064
- detail
100065
- }),
100066
- catch: (e) => cliErrorFromUnknown(e, { code: "DB_UNAVAILABLE" })
100908
+ const result = yield* executeReadOutlineUseCase({
100909
+ id: id4,
100910
+ ref: ref2,
100911
+ depth: depth2,
100912
+ offset: offset6,
100913
+ nodes: nodes2,
100914
+ format: format10,
100915
+ excludeProperties,
100916
+ includeEmpty,
100917
+ expandReferences,
100918
+ maxReferenceDepth: maxReferenceDepth2,
100919
+ detail
100067
100920
  });
100068
100921
  yield* writeSuccess({ data: result, md: result.markdown ?? "" });
100069
100922
  }).pipe(catchAll2(writeFailure)));
@@ -100095,13 +100948,12 @@ var readPageIdCommand = exports_Command.make("page-id", { ref: ref2, id: id4, ma
100095
100948
  ]
100096
100949
  }));
100097
100950
  }
100951
+ const dbPath = cfg.remnoteDb ?? (yield* requireResolvedWorkspace({ ref: hasRef ? ref3 : undefined })).dbPath;
100098
100952
  const link3 = hasRef ? tryParseRemnoteLinkFromRef(ref3) : undefined;
100099
- const ids3 = hasRef ? [link3?.remId ?? (yield* refs.resolve(ref3))] : id5.map(String);
100953
+ const ids3 = hasRef ? [link3?.remId ?? (yield* refs.resolve(ref3, { dbPath }))] : id5.map(String);
100100
100954
  if (ids3.length === 0) {
100101
100955
  return yield* fail8(new CliError({ code: "INVALID_ARGS", message: "Provide at least one Rem ID via --id", exitCode: 2 }));
100102
100956
  }
100103
- const inferredDbPath = link3?.workspaceId ? tryResolveRemnoteDbPathForWorkspaceIdSync(link3.workspaceId) : undefined;
100104
- const dbPath = cfg.remnoteDb ?? inferredDbPath;
100105
100957
  const result = yield* tryPromise2({
100106
100958
  try: async () => await executeResolveRemPage({
100107
100959
  ids: ids3,
@@ -101813,34 +102665,15 @@ var readSearchCommand = exports_Command.make("search", {
101813
102665
  limit: limit8,
101814
102666
  offset: offset7,
101815
102667
  timeoutMs: effectiveTimeoutMs
101816
- }) : yield* tryPromise2({
101817
- try: async () => await executeSearchRemOverview({
101818
- query: query2,
101819
- dbPath: cfg.remnoteDb,
101820
- timeRange: timeRange3,
101821
- parentId: parentId2,
101822
- pagesOnly,
101823
- excludePages,
101824
- limit: limit8,
101825
- offset: offset7,
101826
- timeoutMs: effectiveTimeoutMs
101827
- }),
101828
- catch: (e) => {
101829
- if (e?.code === "TIMEOUT") {
101830
- return new CliError({
101831
- code: "TIMEOUT",
101832
- message: `DB query timed out after ${effectiveTimeoutMs}ms`,
101833
- exitCode: 1,
101834
- details: { timeoutMs: effectiveTimeoutMs },
101835
- hint: [
101836
- "Narrow the search scope (e.g. add --time 30d, or --parent <remId>)",
101837
- "Reduce the result count (e.g. --limit 10)",
101838
- 'Try plugin candidates: agent-remnote plugin search --query "<keywords>"'
101839
- ]
101840
- });
101841
- }
101842
- return cliErrorFromUnknown(e, { code: "DB_UNAVAILABLE" });
101843
- }
102668
+ }) : yield* executeDbSearchUseCase({
102669
+ query: query2,
102670
+ timeRange: timeRange3,
102671
+ parentId: parentId2,
102672
+ pagesOnly,
102673
+ excludePages,
102674
+ limit: limit8,
102675
+ offset: offset7,
102676
+ timeoutMs: effectiveTimeoutMs
101844
102677
  });
101845
102678
  yield* writeSuccess({ data: result, md: result.markdown ?? "" });
101846
102679
  }).pipe(catchAll2(writeFailure)));
@@ -102047,6 +102880,8 @@ var writeTableOptionAddCommand = exports_Command.make("add", {
102047
102880
  exitCode: 2
102048
102881
  }));
102049
102882
  }
102883
+ const cfg = yield* AppConfig;
102884
+ const hostApi = yield* HostApiClient;
102050
102885
  const payloadSvc = yield* Payload;
102051
102886
  const op2 = yield* try_3({
102052
102887
  try: () => normalizeOp({ type: "add_option", payload: { propertyId: property, text: text16 } }, payloadSvc.normalizeKeys),
@@ -102058,6 +102893,53 @@ var writeTableOptionAddCommand = exports_Command.make("add", {
102058
102893
  })
102059
102894
  });
102060
102895
  const metaValue = meta ? yield* payloadSvc.readJson(meta) : undefined;
102896
+ if (cfg.apiBaseUrl) {
102897
+ if (dryRun) {
102898
+ yield* writeSuccess({
102899
+ data: { dry_run: true, ops: [op2], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
102900
+ md: `- dry_run: true
102901
+ - op: add_option
102902
+ - property_id: ${property}
102903
+ `
102904
+ });
102905
+ return;
102906
+ }
102907
+ const data2 = yield* hostApi.writeApply({
102908
+ baseUrl: cfg.apiBaseUrl,
102909
+ body: {
102910
+ version: 1,
102911
+ kind: "ops",
102912
+ ops: [op2],
102913
+ priority: priority5,
102914
+ clientId: clientId5,
102915
+ idempotencyKey: idempotencyKey5,
102916
+ meta: metaValue,
102917
+ notify: notify5,
102918
+ ensureDaemon: ensureDaemon7
102919
+ }
102920
+ });
102921
+ const waited2 = wait5 ? yield* hostApi.queueWait({
102922
+ baseUrl: cfg.apiBaseUrl,
102923
+ txnId: String(data2.txn_id),
102924
+ timeoutMs: timeoutMs7,
102925
+ pollMs: pollMs5
102926
+ }) : null;
102927
+ const out2 = waited2 ? { ...data2, ...waited2 } : data2;
102928
+ yield* writeSuccess({
102929
+ data: out2,
102930
+ ids: [data2.txn_id, ...data2.op_ids],
102931
+ md: [
102932
+ `- txn_id: ${data2.txn_id}`,
102933
+ `- op_ids: ${data2.op_ids.length}`,
102934
+ `- notified: ${data2.notified}`,
102935
+ `- sent: ${data2.sent ?? ""}`,
102936
+ ...waited2 ? [`- status: ${waited2.status}`, `- elapsed_ms: ${waited2.elapsed_ms}`] : []
102937
+ ].join(`
102938
+ `)
102939
+ });
102940
+ return;
102941
+ }
102942
+ yield* ensureOptionMutationSupportedForProperty({ scopeLabel: "table", propertyId: property });
102061
102943
  if (dryRun) {
102062
102944
  yield* writeSuccess({
102063
102945
  data: { dry_run: true, ops: [op2], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
@@ -102121,6 +103003,8 @@ var writeTableOptionRemoveCommand = exports_Command.make("remove", {
102121
103003
  exitCode: 2
102122
103004
  }));
102123
103005
  }
103006
+ const cfg = yield* AppConfig;
103007
+ const hostApi = yield* HostApiClient;
102124
103008
  const payloadSvc = yield* Payload;
102125
103009
  const op2 = yield* try_3({
102126
103010
  try: () => normalizeOp({ type: "remove_option", payload: { optionId: option6 } }, payloadSvc.normalizeKeys),
@@ -102132,6 +103016,53 @@ var writeTableOptionRemoveCommand = exports_Command.make("remove", {
102132
103016
  })
102133
103017
  });
102134
103018
  const metaValue = meta ? yield* payloadSvc.readJson(meta) : undefined;
103019
+ if (cfg.apiBaseUrl) {
103020
+ if (dryRun) {
103021
+ yield* writeSuccess({
103022
+ data: { dry_run: true, ops: [op2], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
103023
+ md: `- dry_run: true
103024
+ - op: remove_option
103025
+ - option_id: ${option6}
103026
+ `
103027
+ });
103028
+ return;
103029
+ }
103030
+ const data2 = yield* hostApi.writeApply({
103031
+ baseUrl: cfg.apiBaseUrl,
103032
+ body: {
103033
+ version: 1,
103034
+ kind: "ops",
103035
+ ops: [op2],
103036
+ priority: priority5,
103037
+ clientId: clientId5,
103038
+ idempotencyKey: idempotencyKey5,
103039
+ meta: metaValue,
103040
+ notify: notify5,
103041
+ ensureDaemon: ensureDaemon7
103042
+ }
103043
+ });
103044
+ const waited2 = wait5 ? yield* hostApi.queueWait({
103045
+ baseUrl: cfg.apiBaseUrl,
103046
+ txnId: String(data2.txn_id),
103047
+ timeoutMs: timeoutMs7,
103048
+ pollMs: pollMs5
103049
+ }) : null;
103050
+ const out2 = waited2 ? { ...data2, ...waited2 } : data2;
103051
+ yield* writeSuccess({
103052
+ data: out2,
103053
+ ids: [data2.txn_id, ...data2.op_ids],
103054
+ md: [
103055
+ `- txn_id: ${data2.txn_id}`,
103056
+ `- op_ids: ${data2.op_ids.length}`,
103057
+ `- notified: ${data2.notified}`,
103058
+ `- sent: ${data2.sent ?? ""}`,
103059
+ ...waited2 ? [`- status: ${waited2.status}`, `- elapsed_ms: ${waited2.elapsed_ms}`] : []
103060
+ ].join(`
103061
+ `)
103062
+ });
103063
+ return;
103064
+ }
103065
+ yield* ensureOptionMutationSupportedForOption({ scopeLabel: "table", optionId: option6 });
102135
103066
  if (dryRun) {
102136
103067
  yield* writeSuccess({
102137
103068
  data: { dry_run: true, ops: [op2], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
@@ -102216,6 +103147,11 @@ var writeTablePropertyAddCommand = exports_Command.make("add", {
102216
103147
  exitCode: 2
102217
103148
  }));
102218
103149
  }
103150
+ yield* ensureTypedPropertyCreationSupported({
103151
+ scopeLabel: "table",
103152
+ type: type2,
103153
+ hasOptions: typeof options6 === "string" && options6.trim().length > 0
103154
+ });
102219
103155
  const payloadSvc = yield* Payload;
102220
103156
  const optionsRaw = options6 ? yield* payloadSvc.readJson(options6) : undefined;
102221
103157
  const optionNames = optionsRaw !== undefined ? yield* try_3({
@@ -102296,78 +103232,8 @@ var writeTablePropertySetTypeCommand = exports_Command.make("set-type", {
102296
103232
  clientId: writeCommonOptions.clientId,
102297
103233
  idempotencyKey: writeCommonOptions.idempotencyKey,
102298
103234
  meta: writeCommonOptions.meta
102299
- }, ({
102300
- property,
102301
- type: type2,
102302
- notify: notify5,
102303
- ensureDaemon: ensureDaemon7,
102304
- wait: wait5,
102305
- timeoutMs: timeoutMs7,
102306
- pollMs: pollMs5,
102307
- dryRun,
102308
- priority: priority5,
102309
- clientId: clientId5,
102310
- idempotencyKey: idempotencyKey5,
102311
- meta
102312
- }) => gen2(function* () {
102313
- if (!wait5 && (timeoutMs7 !== undefined || pollMs5 !== undefined)) {
102314
- return yield* fail8(new CliError({
102315
- code: "INVALID_ARGS",
102316
- message: "Use --wait to enable --timeout-ms/--poll-ms",
102317
- exitCode: 2
102318
- }));
102319
- }
102320
- if (dryRun && wait5) {
102321
- return yield* fail8(new CliError({
102322
- code: "INVALID_ARGS",
102323
- message: "--wait is not compatible with --dry-run",
102324
- exitCode: 2
102325
- }));
102326
- }
102327
- const payloadSvc = yield* Payload;
102328
- const op2 = yield* try_3({
102329
- try: () => normalizeOp({ type: "set_property_type", payload: { propertyId: property, type: type2 } }, payloadSvc.normalizeKeys),
102330
- catch: (e) => isCliError(e) ? e : new CliError({
102331
- code: "INVALID_PAYLOAD",
102332
- message: "Failed to generate op",
102333
- exitCode: 2,
102334
- details: { error: String(e?.message || e) }
102335
- })
102336
- });
102337
- const metaValue = meta ? yield* payloadSvc.readJson(meta) : undefined;
102338
- if (dryRun) {
102339
- yield* writeSuccess({
102340
- data: { dry_run: true, ops: [op2], meta: metaValue ? payloadSvc.normalizeKeys(metaValue) : undefined },
102341
- md: `- dry_run: true
102342
- - op: set_property_type
102343
- - property_id: ${property}
102344
- `
102345
- });
102346
- return;
102347
- }
102348
- const data = yield* enqueueOps({
102349
- ops: [op2],
102350
- priority: priority5,
102351
- clientId: clientId5,
102352
- idempotencyKey: idempotencyKey5,
102353
- meta: metaValue,
102354
- notify: notify5,
102355
- ensureDaemon: ensureDaemon7
102356
- });
102357
- const waited = wait5 ? yield* waitForTxn({ txnId: data.txn_id, timeoutMs: timeoutMs7, pollMs: pollMs5 }) : null;
102358
- const out = waited ? { ...data, ...waited } : data;
102359
- yield* writeSuccess({
102360
- data: out,
102361
- ids: [data.txn_id, ...data.op_ids],
102362
- md: [
102363
- `- txn_id: ${data.txn_id}`,
102364
- `- op_ids: ${data.op_ids.length}`,
102365
- `- notified: ${data.notified}`,
102366
- `- sent: ${data.sent ?? ""}`,
102367
- ...waited ? [`- status: ${waited.status}`, `- elapsed_ms: ${waited.elapsed_ms}`] : []
102368
- ].join(`
102369
- `)
102370
- });
103235
+ }, () => gen2(function* () {
103236
+ return yield* failUnsupportedPropertyTypeMutation("table");
102371
103237
  }).pipe(catchAll2(writeFailure)));
102372
103238
 
102373
103239
  // src/commands/write/table/property/index.ts
@@ -103822,7 +104688,7 @@ var stackEnsureCommand = exports_Command.make("ensure", {
103822
104688
  }).pipe(catchAll2(writeFailure)));
103823
104689
 
103824
104690
  // src/commands/stack/status.ts
103825
- import path25 from "node:path";
104691
+ import path26 from "node:path";
103826
104692
  var stackStatusCommand = exports_Command.make("status", {}, () => gen2(function* () {
103827
104693
  const cfg = yield* AppConfig;
103828
104694
  const daemonFiles = yield* DaemonFiles;
@@ -103839,10 +104705,11 @@ var stackStatusCommand = exports_Command.make("status", {}, () => gen2(function*
103839
104705
  const apiPidFile = resolveUserFilePath(apiFiles.defaultPidFile());
103840
104706
  const apiPidInfo = yield* apiFiles.readPidFile(apiPidFile);
103841
104707
  const apiRunning = apiPidInfo ? yield* proc.isPidRunning(apiPidInfo.pid) : false;
103842
- const apiBaseUrl = apiLocalBaseUrl(apiPidInfo?.port ?? cfg.apiPort ?? 3000);
104708
+ const apiBasePath = apiPidInfo?.base_path ?? cfg.apiBasePath ?? "/v1";
104709
+ const apiBaseUrl = apiLocalBaseUrl(apiPidInfo?.port ?? cfg.apiPort ?? 3000, apiBasePath);
103843
104710
  const apiStatus = yield* api.status({ baseUrl: apiBaseUrl, timeoutMs: 2000 }).pipe(either3);
103844
104711
  const queueStats2 = yield* queue.stats({ dbPath: cfg.storeDb }).pipe(either3);
103845
- const stateFilePath = resolveUserFilePath(daemonPidInfo?.state_file ?? path25.join(path25.dirname(daemonPidFile), "ws.state.json"));
104712
+ const stateFilePath = resolveUserFilePath(daemonPidInfo?.state_file ?? path26.join(path26.dirname(daemonPidFile), "ws.state.json"));
103846
104713
  const data = {
103847
104714
  daemon: {
103848
104715
  running: daemonRunning,
@@ -103857,6 +104724,7 @@ var stackStatusCommand = exports_Command.make("status", {}, () => gen2(function*
103857
104724
  pid: apiPidInfo?.pid ?? null,
103858
104725
  pid_file: apiPidFile,
103859
104726
  base_url: apiBaseUrl,
104727
+ base_path: apiBasePath,
103860
104728
  healthy: apiStatus._tag === "Right",
103861
104729
  status: apiStatus._tag === "Right" ? apiStatus.right : null
103862
104730
  },
@@ -103871,6 +104739,13 @@ var stackStatusCommand = exports_Command.make("status", {}, () => gen2(function*
103871
104739
  `- api_pid: ${data.api.pid ?? ""}`,
103872
104740
  `- api_healthy: ${data.api.healthy}`,
103873
104741
  `- api_base_url: ${data.api.base_url}`,
104742
+ `- api_base_path: ${data.api.base_path}`,
104743
+ `- db_read_ready: ${data.api.status?.capabilities?.db_read_ready ?? ""}`,
104744
+ `- plugin_rpc_ready: ${data.api.status?.capabilities?.plugin_rpc_ready ?? ""}`,
104745
+ `- write_ready: ${data.api.status?.capabilities?.write_ready ?? ""}`,
104746
+ `- ui_session_ready: ${data.api.status?.capabilities?.ui_session_ready ?? ""}`,
104747
+ `- workspace_resolved: ${data.api.status?.workspace?.resolved ?? ""}`,
104748
+ `- current_workspace_id: ${data.api.status?.workspace?.currentWorkspaceId ?? ""}`,
103874
104749
  `- active_worker_conn_id: ${data.active_worker_conn_id ?? ""}`,
103875
104750
  `- queue_pending: ${data.queue?.pending ?? ""}`,
103876
104751
  `- queue_in_flight: ${data.queue?.in_flight ?? ""}`
@@ -103880,7 +104755,7 @@ var stackStatusCommand = exports_Command.make("status", {}, () => gen2(function*
103880
104755
  }).pipe(catchAll2(writeFailure)));
103881
104756
 
103882
104757
  // src/commands/stack/stop.ts
103883
- import path26 from "node:path";
104758
+ import path27 from "node:path";
103884
104759
  var stackStopCommand = exports_Command.make("stop", {}, () => gen2(function* () {
103885
104760
  const apiFiles = yield* ApiDaemonFiles;
103886
104761
  const daemonFiles = yield* DaemonFiles;
@@ -103917,7 +104792,7 @@ var stackStopCommand = exports_Command.make("stop", {}, () => gen2(function* ()
103917
104792
  }
103918
104793
  }
103919
104794
  yield* daemonFiles.deletePidFile(daemonPidFile).pipe(catchAll2(() => _void));
103920
- yield* supervisorState.deleteStateFile(resolveUserFilePath(daemonPidInfo?.state_file ?? path26.join(path26.dirname(daemonPidFile), "ws.state.json"))).pipe(catchAll2(() => _void));
104795
+ yield* supervisorState.deleteStateFile(resolveUserFilePath(daemonPidInfo?.state_file ?? path27.join(path27.dirname(daemonPidFile), "ws.state.json"))).pipe(catchAll2(() => _void));
103921
104796
  yield* writeSuccess({
103922
104797
  data: { stopped: true, api_stopped: apiStopped, daemon_stopped: daemonStopped },
103923
104798
  md: `- stopped: true
@@ -103947,7 +104822,7 @@ var configFile = text9("config-file").pipe(optional5, map34(optionToUndefined49)
103947
104822
  var appConfigLive = effect(AppConfig, resolveConfig());
103948
104823
  var statusLineUpdaterLive = StatusLineUpdaterLive.pipe(provide3([appConfigLive, QueueLive, StatusLineFileLive, TmuxLive, WsBridgeStateLive]));
103949
104824
  var statusLineLive = StatusLineControllerLive.pipe(provide3(statusLineUpdaterLive), provide3(appConfigLive));
103950
- var servicesLive = mergeAll5(appConfigLive, OutputLive, FileInputLive, FsAccessLive, LogWriterFactoryLive, PayloadLive, DaemonFilesLive, ApiDaemonFilesLive, ProcessLive, ChildProcessLive, WsClientLive, HostApiClientLive, UserConfigFileLive, QueueLive, RefResolverLive, RemDbLive, StatusLineFileLive, SubprocessLive, SupervisorStateLive, WsBridgeServerLive, WsBridgeStateFileLive, statusLineLive);
104825
+ var servicesLive = mergeAll5(appConfigLive, OutputLive, FileInputLive, FsAccessLive, LogWriterFactoryLive, PayloadLive, DaemonFilesLive, ApiDaemonFilesLive, ProcessLive, ChildProcessLive, WsClientLive, HostApiClientLive.pipe(provide3(appConfigLive)), UserConfigFileLive, WorkspaceBindingsLive, QueueLive, RefResolverLive, RemDbLive, StatusLineFileLive, SubprocessLive, SupervisorStateLive, WsBridgeServerLive, WsBridgeStateFileLive, statusLineLive);
103951
104826
  var rootCommand = exports_Command.make("agent-remnote", {
103952
104827
  json: boolean8("json"),
103953
104828
  md: boolean8("md"),