office-core 0.1.0 → 0.1.2
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
|
-
|
|
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 = {};
|
|
@@ -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,32 +123,73 @@ function findCmd(name) {
|
|
|
117
123
|
// ─── Slash Menu Renderer ────────────────────────────────────────────────────
|
|
118
124
|
let _menuLines = 0;
|
|
119
125
|
let _menuSelection = 0;
|
|
126
|
+
let _menuScrollOffset = 0;
|
|
127
|
+
let _menuQuery = "";
|
|
128
|
+
const MAX_VISIBLE_SLASH_ITEMS = 6;
|
|
120
129
|
function getSlashMatches(partial) {
|
|
121
130
|
const q = partial.toLowerCase();
|
|
122
131
|
return CMDS.filter((c) => c.name.startsWith(q) || c.alias?.some((a) => a.startsWith(q)));
|
|
123
132
|
}
|
|
124
|
-
function
|
|
125
|
-
|
|
133
|
+
function eraseSlashMenu() {
|
|
134
|
+
if (_menuLines === 0)
|
|
135
|
+
return;
|
|
136
|
+
process.stdout.write(`\x1b[s\n`);
|
|
137
|
+
clearScreenDown(process.stdout);
|
|
138
|
+
process.stdout.write(`\x1b[u`);
|
|
139
|
+
_menuLines = 0;
|
|
140
|
+
}
|
|
141
|
+
function resetSlashMenuState() {
|
|
142
|
+
_menuSelection = 0;
|
|
143
|
+
_menuScrollOffset = 0;
|
|
144
|
+
_menuQuery = "";
|
|
145
|
+
}
|
|
146
|
+
function renderSlashMenu(partial, preserveSelection = false) {
|
|
147
|
+
eraseSlashMenu();
|
|
148
|
+
if (!preserveSelection || partial !== _menuQuery) {
|
|
149
|
+
_menuSelection = 0;
|
|
150
|
+
_menuScrollOffset = 0;
|
|
151
|
+
}
|
|
152
|
+
_menuQuery = partial;
|
|
126
153
|
const hits = getSlashMatches(partial);
|
|
127
|
-
if (hits.length === 0)
|
|
154
|
+
if (hits.length === 0) {
|
|
155
|
+
resetSlashMenuState();
|
|
128
156
|
return;
|
|
157
|
+
}
|
|
129
158
|
_menuSelection = Math.max(0, Math.min(_menuSelection, hits.length - 1));
|
|
130
|
-
|
|
131
|
-
|
|
159
|
+
if (_menuSelection < _menuScrollOffset) {
|
|
160
|
+
_menuScrollOffset = _menuSelection;
|
|
161
|
+
}
|
|
162
|
+
if (_menuSelection >= _menuScrollOffset + MAX_VISIBLE_SLASH_ITEMS) {
|
|
163
|
+
_menuScrollOffset = _menuSelection - MAX_VISIBLE_SLASH_ITEMS + 1;
|
|
164
|
+
}
|
|
165
|
+
const visibleHits = hits.slice(_menuScrollOffset, _menuScrollOffset + MAX_VISIBLE_SLASH_ITEMS);
|
|
166
|
+
const lines = [];
|
|
167
|
+
const terminalWidth = Math.max(40, process.stdout.columns ?? 100);
|
|
168
|
+
const commandWidth = Math.min(28, Math.max(16, Math.floor(terminalWidth * 0.32)));
|
|
169
|
+
const descWidth = Math.max(12, terminalWidth - commandWidth - 10);
|
|
170
|
+
if (_menuScrollOffset > 0) {
|
|
171
|
+
lines.push(` ${DIM}${truncateText("↑ more commands", terminalWidth - 4)}${R}`);
|
|
172
|
+
}
|
|
173
|
+
lines.push(...visibleHits.map((c) => {
|
|
174
|
+
const commandLabel = `/${c.name}${c.args ? ` ${c.args}` : ""}`;
|
|
175
|
+
const truncatedCommandLabel = truncateText(commandLabel, commandWidth);
|
|
176
|
+
const truncatedDesc = truncateText(c.desc, descWidth);
|
|
132
177
|
return c === hits[_menuSelection]
|
|
133
|
-
? ` ${BOLD}${WHT}>
|
|
134
|
-
: ` ${GRN}
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
|
|
178
|
+
? ` ${BOLD}${WHT}> ${truncatedCommandLabel.padEnd(commandWidth)}${R} ${DIM}${truncatedDesc}${R}`
|
|
179
|
+
: ` ${GRN}${truncatedCommandLabel.padEnd(commandWidth)}${R} ${DIM}${truncatedDesc}${R}`;
|
|
180
|
+
}));
|
|
181
|
+
if (_menuScrollOffset + visibleHits.length < hits.length) {
|
|
182
|
+
lines.push(` ${DIM}${truncateText("↓ more commands", terminalWidth - 4)}${R}`);
|
|
183
|
+
}
|
|
184
|
+
process.stdout.write(`\x1b[s\n`);
|
|
185
|
+
clearScreenDown(process.stdout);
|
|
186
|
+
process.stdout.write(lines.join("\n"));
|
|
187
|
+
process.stdout.write(`\x1b[u`);
|
|
138
188
|
_menuLines = lines.length;
|
|
139
189
|
}
|
|
140
190
|
function clearSlashMenu() {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
process.stdout.write(`\x1b7\n\x1b[J\x1b8`);
|
|
144
|
-
_menuLines = 0;
|
|
145
|
-
_menuSelection = 0;
|
|
191
|
+
eraseSlashMenu();
|
|
192
|
+
resetSlashMenuState();
|
|
146
193
|
}
|
|
147
194
|
// ─── Tab Completer ──────────────────────────────────────────────────────────
|
|
148
195
|
function completer(line) {
|
|
@@ -426,7 +473,16 @@ async function cmdSetup(_a, ctx) {
|
|
|
426
473
|
if (!resp.ok)
|
|
427
474
|
throw new Error(`${resp.status} ${await resp.text()}`);
|
|
428
475
|
const reg = (await resp.json());
|
|
429
|
-
await upsertHostConfig({
|
|
476
|
+
await upsertHostConfig({
|
|
477
|
+
host_id: reg.host_id,
|
|
478
|
+
base_url,
|
|
479
|
+
project_id,
|
|
480
|
+
workdir,
|
|
481
|
+
display_name,
|
|
482
|
+
token: reg.host_token,
|
|
483
|
+
room_cursor_seq: Number(reg.room_cursor_seq ?? 0),
|
|
484
|
+
poll_ms: 900,
|
|
485
|
+
});
|
|
430
486
|
console.log(` ${GRN}Registered!${R} Host ID: ${reg.host_id}`);
|
|
431
487
|
console.log(` ${DIM}${getHostConfigPath()}${R}`);
|
|
432
488
|
// Connect immediately
|
|
@@ -567,16 +623,16 @@ async function main() {
|
|
|
567
623
|
key.name === "up"
|
|
568
624
|
? (_menuSelection + hits.length - 1) % hits.length
|
|
569
625
|
: (_menuSelection + 1) % hits.length;
|
|
570
|
-
renderSlashMenu(line.slice(1));
|
|
626
|
+
renderSlashMenu(line.slice(1), true);
|
|
571
627
|
return;
|
|
572
628
|
}
|
|
573
629
|
if (hits.length > 0 && key?.name === "tab") {
|
|
574
630
|
const selected = hits[_menuSelection] ?? hits[0];
|
|
575
631
|
replaceInputLine(rl, `/${selected.name} `);
|
|
576
|
-
renderSlashMenu(selected.name);
|
|
632
|
+
renderSlashMenu(selected.name, false);
|
|
577
633
|
return;
|
|
578
634
|
}
|
|
579
|
-
renderSlashMenu(line.slice(1));
|
|
635
|
+
renderSlashMenu(line.slice(1), false);
|
|
580
636
|
}
|
|
581
637
|
else {
|
|
582
638
|
clearSlashMenu();
|