office-core 0.1.0 → 0.1.1

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.
@@ -103,6 +103,7 @@ async function registerHostToken(input) {
103
103
  project_id: registration.project_id,
104
104
  workdir: input.workdir,
105
105
  token: registration.host_token,
106
+ room_cursor_seq: Number(registration.room_cursor_seq ?? 0),
106
107
  poll_ms: input.pollMs,
107
108
  });
108
109
  return registration.host_token;
@@ -293,7 +294,6 @@ async function switchProject(runtime, command) {
293
294
  sessions: [],
294
295
  }).catch(() => undefined);
295
296
  runtime.projectId = targetProjectId;
296
- runtime.roomCursorSeq = 0;
297
297
  runtime.token = await registerHostToken({
298
298
  baseUrl: runtime.baseUrl,
299
299
  projectId: runtime.projectId,
@@ -302,6 +302,8 @@ async function switchProject(runtime, command) {
302
302
  workdir: runtime.defaultWorkdir,
303
303
  pollMs: runtime.pollMs,
304
304
  });
305
+ const refreshedConfig = await loadHostConfig();
306
+ runtime.roomCursorSeq = Number(refreshedConfig?.room_cursor_seq ?? 0);
305
307
  const activeWorkdir = resolveProjectWorkdir(runtime.defaultWorkdir, runtime.projectId);
306
308
  await mkdir(activeWorkdir, { recursive: true });
307
309
  await upsertHostConfig({
@@ -1277,7 +1279,17 @@ export async function postJson(runtime, pathname, body, tokenOverride) {
1277
1279
  });
1278
1280
  }
1279
1281
  export async function postRoomMessageUpsert(runtime, body) {
1280
- return postJson(runtime, `/api/projects/${runtime.projectId}/room/messages/upsert`, body);
1282
+ try {
1283
+ return await postJson(runtime, `/api/projects/${runtime.projectId}/room/messages/upsert`, body);
1284
+ }
1285
+ catch (error) {
1286
+ const message = error instanceof Error ? error.message : String(error);
1287
+ if (!message.includes("/room/messages/upsert failed: 404")) {
1288
+ throw error;
1289
+ }
1290
+ const { message_id: _messageId, ...legacyBody } = body;
1291
+ return postJson(runtime, `/api/projects/${runtime.projectId}/room/messages`, legacyBody);
1292
+ }
1281
1293
  }
1282
1294
  function parseArgs(argv) {
1283
1295
  const result = {};
@@ -108,7 +108,7 @@ async function runInstallFlow(existing) {
108
108
  workdir,
109
109
  display_name,
110
110
  token: registration.host_token,
111
- room_cursor_seq: 0,
111
+ room_cursor_seq: Number(registration.room_cursor_seq ?? 0),
112
112
  poll_ms,
113
113
  auto_start,
114
114
  });
@@ -40,7 +40,7 @@ async function main() {
40
40
  workdir: args.workdir,
41
41
  display_name: displayName,
42
42
  token: registration.host_token,
43
- room_cursor_seq: 0,
43
+ room_cursor_seq: Number(registration.room_cursor_seq ?? 0),
44
44
  poll_ms: args.pollMs ? Number(args.pollMs) : undefined,
45
45
  auto_start: args.autoStart ? args.autoStart !== "false" : undefined,
46
46
  });
@@ -1,4 +1,4 @@
1
- import { createInterface, emitKeypressEvents } from "node:readline";
1
+ import { createInterface, emitKeypressEvents, clearScreenDown } from "node:readline";
2
2
  import { spawn } from "node:child_process";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
@@ -44,6 +44,12 @@ function printBanner() {
44
44
  function printHeader(label) {
45
45
  console.log(` ${BOLD}${WHT}office core${R} ${DIM}-${R} ${BOLD}${label}${R}`);
46
46
  }
47
+ function truncateText(value, maxLength) {
48
+ if (maxLength <= 1) {
49
+ return value.slice(0, Math.max(0, maxLength));
50
+ }
51
+ return value.length <= maxLength ? value : `${value.slice(0, maxLength - 1)}…`;
52
+ }
47
53
  // ─── Box Drawing ────────────────────────────────────────────────────────────
48
54
  function wordWrap(text, width) {
49
55
  const out = [];
@@ -117,6 +123,8 @@ function findCmd(name) {
117
123
  // ─── Slash Menu Renderer ────────────────────────────────────────────────────
118
124
  let _menuLines = 0;
119
125
  let _menuSelection = 0;
126
+ let _menuScrollOffset = 0;
127
+ const MAX_VISIBLE_SLASH_ITEMS = 6;
120
128
  function getSlashMatches(partial) {
121
129
  const q = partial.toLowerCase();
122
130
  return CMDS.filter((c) => c.name.startsWith(q) || c.alias?.some((a) => a.startsWith(q)));
@@ -127,22 +135,46 @@ function renderSlashMenu(partial) {
127
135
  if (hits.length === 0)
128
136
  return;
129
137
  _menuSelection = Math.max(0, Math.min(_menuSelection, hits.length - 1));
130
- const lines = hits.map((c) => {
131
- const args = c.args ? ` ${GRY}${c.args}${R}` : "";
138
+ if (_menuSelection < _menuScrollOffset) {
139
+ _menuScrollOffset = _menuSelection;
140
+ }
141
+ if (_menuSelection >= _menuScrollOffset + MAX_VISIBLE_SLASH_ITEMS) {
142
+ _menuScrollOffset = _menuSelection - MAX_VISIBLE_SLASH_ITEMS + 1;
143
+ }
144
+ const visibleHits = hits.slice(_menuScrollOffset, _menuScrollOffset + MAX_VISIBLE_SLASH_ITEMS);
145
+ const lines = [];
146
+ const terminalWidth = Math.max(40, process.stdout.columns ?? 100);
147
+ const commandWidth = Math.min(28, Math.max(16, Math.floor(terminalWidth * 0.32)));
148
+ const descWidth = Math.max(12, terminalWidth - commandWidth - 10);
149
+ if (_menuScrollOffset > 0) {
150
+ lines.push(` ${DIM}${truncateText("↑ more commands", terminalWidth - 4)}${R}`);
151
+ }
152
+ lines.push(...visibleHits.map((c) => {
153
+ const commandLabel = `/${c.name}${c.args ? ` ${c.args}` : ""}`;
154
+ const truncatedCommandLabel = truncateText(commandLabel, commandWidth);
155
+ const truncatedDesc = truncateText(c.desc, descWidth);
132
156
  return c === hits[_menuSelection]
133
- ? ` ${BOLD}${WHT}> /${c.name}${R}${args} ${DIM}${c.desc}${R}`
134
- : ` ${GRN}/${c.name}${R}${args} ${DIM}${c.desc}${R}`;
135
- });
136
- // Save cursor, newline, print menu, restore cursor
137
- process.stdout.write(`\x1b7\n${lines.join("\n")}\x1b8`);
157
+ ? ` ${BOLD}${WHT}> ${truncatedCommandLabel.padEnd(commandWidth)}${R} ${DIM}${truncatedDesc}${R}`
158
+ : ` ${GRN}${truncatedCommandLabel.padEnd(commandWidth)}${R} ${DIM}${truncatedDesc}${R}`;
159
+ }));
160
+ if (_menuScrollOffset + visibleHits.length < hits.length) {
161
+ lines.push(` ${DIM}${truncateText("↓ more commands", terminalWidth - 4)}${R}`);
162
+ }
163
+ process.stdout.write(`\x1b[s\n`);
164
+ clearScreenDown(process.stdout);
165
+ process.stdout.write(lines.join("\n"));
166
+ process.stdout.write(`\x1b[u`);
138
167
  _menuLines = lines.length;
139
168
  }
140
169
  function clearSlashMenu() {
141
170
  if (_menuLines === 0)
142
171
  return;
143
- process.stdout.write(`\x1b7\n\x1b[J\x1b8`);
172
+ process.stdout.write(`\x1b[s\n`);
173
+ clearScreenDown(process.stdout);
174
+ process.stdout.write(`\x1b[u`);
144
175
  _menuLines = 0;
145
176
  _menuSelection = 0;
177
+ _menuScrollOffset = 0;
146
178
  }
147
179
  // ─── Tab Completer ──────────────────────────────────────────────────────────
148
180
  function completer(line) {
@@ -426,7 +458,16 @@ async function cmdSetup(_a, ctx) {
426
458
  if (!resp.ok)
427
459
  throw new Error(`${resp.status} ${await resp.text()}`);
428
460
  const reg = (await resp.json());
429
- await upsertHostConfig({ host_id: reg.host_id, base_url, project_id, workdir, display_name, token: reg.host_token, room_cursor_seq: 0, poll_ms: 900 });
461
+ await upsertHostConfig({
462
+ host_id: reg.host_id,
463
+ base_url,
464
+ project_id,
465
+ workdir,
466
+ display_name,
467
+ token: reg.host_token,
468
+ room_cursor_seq: Number(reg.room_cursor_seq ?? 0),
469
+ poll_ms: 900,
470
+ });
430
471
  console.log(` ${GRN}Registered!${R} Host ID: ${reg.host_id}`);
431
472
  console.log(` ${DIM}${getHostConfigPath()}${R}`);
432
473
  // Connect immediately
@@ -328,6 +328,7 @@ export class ProjectDO {
328
328
  host_id: requestedHostId,
329
329
  host_token: hostToken,
330
330
  project_id: body.project_id,
331
+ room_cursor_seq: next.room.next_seq,
331
332
  room_settings: next.room.settings,
332
333
  });
333
334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "office-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Installable CLI and local host runtime for Office Core",
5
5
  "type": "module",
6
6
  "bin": {