@sixb/cli-core 0.1.10 → 0.2.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.
- package/dist/commands/history.d.ts +7 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/metadata.d.ts +1 -0
- package/dist/commands/metadata.d.ts.map +1 -1
- package/dist/index.js +2702 -9
- package/dist/index.js.map +5 -5
- package/dist/policies.d.ts +5 -0
- package/dist/policies.d.ts.map +1 -1
- package/dist/sandbox-cli.d.ts +1 -1
- package/dist/sandbox-cli.d.ts.map +1 -1
- package/package.json +1 -13
- package/src/commands/history.ts +204 -0
- package/src/commands/metadata.ts +7 -0
- package/src/generated/sixb.mjs +111 -0
- package/src/main.ts +4 -2
- package/src/policies.ts +2 -0
- package/src/sandbox-cli-assets.d.ts +10 -0
- package/src/sandbox-cli.ts +7 -9
- package/dist/bin/sixb +0 -26
- package/dist/generated/sixb.mjs +0 -2547
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import type { ApiClient } from "../api-client"
|
|
2
|
+
import {
|
|
3
|
+
integerInRange,
|
|
4
|
+
isHelp,
|
|
5
|
+
nonNegativeInteger,
|
|
6
|
+
parseCommandArgs,
|
|
7
|
+
requestsHelp,
|
|
8
|
+
} from "../arguments"
|
|
9
|
+
import { CliError, EXIT_API, fail, writeText } from "../output"
|
|
10
|
+
import { CLI_LIMITS } from "../policies"
|
|
11
|
+
import { GROUP_HELP } from "./metadata"
|
|
12
|
+
import { idSegment } from "./shared"
|
|
13
|
+
|
|
14
|
+
/** In a page, longer text is cut: `sixb history <seq>` prints a message whole. */
|
|
15
|
+
const PAGE_TEXT_CHARS = 4_000
|
|
16
|
+
const PAGE_TOOL_CHARS = 1_000
|
|
17
|
+
|
|
18
|
+
interface StoredMessage {
|
|
19
|
+
readonly id?: string
|
|
20
|
+
readonly seq: number
|
|
21
|
+
readonly role: string
|
|
22
|
+
readonly createdAt?: string
|
|
23
|
+
readonly authorPrincipal?: { readonly type: string; readonly id: string }
|
|
24
|
+
readonly parts: readonly Record<string, unknown>[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface MessagePage {
|
|
28
|
+
readonly messages: readonly StoredMessage[]
|
|
29
|
+
readonly authors: readonly { readonly id: string; readonly displayName?: string }[]
|
|
30
|
+
readonly hasMore: boolean
|
|
31
|
+
readonly total: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Read the run's conversation as text, oldest first, compacted turns included: what was said,
|
|
36
|
+
* the files sent, and each tool call with its result. Reasoning stays out.
|
|
37
|
+
*/
|
|
38
|
+
export async function history(api: ApiClient, args: readonly string[]): Promise<void> {
|
|
39
|
+
if (isHelp(args[0]) || requestsHelp(args)) return writeText(GROUP_HELP.history)
|
|
40
|
+
|
|
41
|
+
const { positionals, options } = parseCommandArgs(
|
|
42
|
+
args,
|
|
43
|
+
{ "--offset": "string", "--limit": "string" },
|
|
44
|
+
"history",
|
|
45
|
+
[0, 1]
|
|
46
|
+
)
|
|
47
|
+
const threadId = process.env.SIXB_THREAD_ID
|
|
48
|
+
if (!threadId) fail("This run has no conversation to read: SIXB_THREAD_ID is not set.")
|
|
49
|
+
const path = `/api/agent-threads/${idSegment(threadId)}/messages`
|
|
50
|
+
|
|
51
|
+
const seq = positionals[0]
|
|
52
|
+
if (seq !== undefined) {
|
|
53
|
+
const wanted = integerInRange("<seq>", seq, 1, Number.MAX_SAFE_INTEGER)
|
|
54
|
+
const page = messagePage(
|
|
55
|
+
await api.get(path, { order: "asc", offset: String(wanted - 1), limit: "1" })
|
|
56
|
+
)
|
|
57
|
+
const message = page.messages[0]
|
|
58
|
+
if (message?.seq !== wanted) {
|
|
59
|
+
throw new CliError(
|
|
60
|
+
{ code: "not_found", message: `The conversation has no message #${wanted}.` },
|
|
61
|
+
EXIT_API
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
return writeText(renderMessage(message, page, { whole: true }))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const offset = nonNegativeInteger("--offset", options["--offset"] ?? "0")
|
|
68
|
+
const limit = integerInRange(
|
|
69
|
+
"--limit",
|
|
70
|
+
options["--limit"] ?? String(CLI_LIMITS.history.default),
|
|
71
|
+
1,
|
|
72
|
+
CLI_LIMITS.history.maximum
|
|
73
|
+
)
|
|
74
|
+
const page = messagePage(
|
|
75
|
+
await api.get(path, { order: "asc", offset: String(offset), limit: String(limit) })
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
const shown = page.messages.map((message) => renderMessage(message, page, { whole: false }))
|
|
79
|
+
const end = offset + page.messages.length
|
|
80
|
+
const footer =
|
|
81
|
+
page.messages.length === 0
|
|
82
|
+
? `No messages from offset ${offset}: the conversation has ${page.total}.`
|
|
83
|
+
: page.hasMore
|
|
84
|
+
? `Messages ${offset + 1}–${end} of ${page.total}. Next: sixb history --offset ${end}`
|
|
85
|
+
: `Messages ${offset + 1}–${end} of ${page.total}: the end of the conversation.`
|
|
86
|
+
writeText([...shown, footer].join("\n\n"))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderMessage(
|
|
90
|
+
message: StoredMessage,
|
|
91
|
+
page: MessagePage,
|
|
92
|
+
options: { readonly whole: boolean }
|
|
93
|
+
): string {
|
|
94
|
+
const author = message.authorPrincipal
|
|
95
|
+
? page.authors.find((person) => person.id === message.authorPrincipal?.id)?.displayName
|
|
96
|
+
: undefined
|
|
97
|
+
const heading = [`#${message.seq} ${message.role}`, author, message.createdAt]
|
|
98
|
+
.filter(Boolean)
|
|
99
|
+
.join(" · ")
|
|
100
|
+
const bounds = {
|
|
101
|
+
seq: message.seq,
|
|
102
|
+
textChars: options.whole ? Number.POSITIVE_INFINITY : PAGE_TEXT_CHARS,
|
|
103
|
+
toolChars: options.whole ? Number.POSITIVE_INFINITY : PAGE_TOOL_CHARS,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return [
|
|
107
|
+
`── ${heading}`,
|
|
108
|
+
...message.parts.flatMap((part, index) => [
|
|
109
|
+
...renderPart(part, bounds),
|
|
110
|
+
...renderOutputFile(part, message, index),
|
|
111
|
+
]),
|
|
112
|
+
].join("\n")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Where a call's whole output is, when it was too large for the context and its part keeps a
|
|
117
|
+
* bounded copy: in the sandbox while the conversation still shows the call, which every run
|
|
118
|
+
* writes back, and through the message's file route whatever happened since.
|
|
119
|
+
*/
|
|
120
|
+
function renderOutputFile(
|
|
121
|
+
part: Record<string, unknown>,
|
|
122
|
+
message: StoredMessage,
|
|
123
|
+
index: number
|
|
124
|
+
): string[] {
|
|
125
|
+
const file = part.type === "tool-call" ? record(part.outputFile) : undefined
|
|
126
|
+
const fileRef = record(file?.fileRef)
|
|
127
|
+
if (typeof file?.path !== "string" || typeof fileRef?.sizeBytes !== "number") return []
|
|
128
|
+
|
|
129
|
+
const contextDir = process.env.SIXB_CONTEXT_DIR
|
|
130
|
+
const path = contextDir ? `${contextDir.replace(/\/$/, "")}/${file.path}` : file.path
|
|
131
|
+
const apiBaseUrl = process.env.SIXB_API_BASE_URL
|
|
132
|
+
const threadId = process.env.SIXB_THREAD_ID
|
|
133
|
+
const download =
|
|
134
|
+
apiBaseUrl && threadId && message.id
|
|
135
|
+
? `; else: sixb files download url ${apiBaseUrl.replace(/\/$/, "")}/api/agent-threads/${idSegment(threadId)}/messages/${idSegment(message.id)}/files/content?path=${encodeURIComponent(`/parts/${index}/outputFile/fileRef`)} --output <file>`
|
|
136
|
+
: ""
|
|
137
|
+
return [
|
|
138
|
+
` [whole output, ${fileRef.sizeBytes.toLocaleString("en-US")} bytes: ${path}, written back to the sandbox at each run while the conversation shows this call${download}]`,
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function renderPart(
|
|
143
|
+
part: Record<string, unknown>,
|
|
144
|
+
bounds: { readonly seq: number; readonly textChars: number; readonly toolChars: number }
|
|
145
|
+
): string[] {
|
|
146
|
+
const cut = (value: string, max: number) =>
|
|
147
|
+
value.length <= max
|
|
148
|
+
? value
|
|
149
|
+
: `${value.slice(0, max)}… [${(value.length - max).toLocaleString("en-US")} more characters: sixb history ${bounds.seq}]`
|
|
150
|
+
|
|
151
|
+
switch (part.type) {
|
|
152
|
+
case "text":
|
|
153
|
+
return typeof part.text === "string" && part.text.trim()
|
|
154
|
+
? [cut(part.text, bounds.textChars)]
|
|
155
|
+
: []
|
|
156
|
+
case "file": {
|
|
157
|
+
const file = record(part.fileRef)
|
|
158
|
+
const name = file?.fileName ?? file?.logicalPath ?? file?.blobId
|
|
159
|
+
return [`[file ${String(name)}${file?.mediaType ? `, ${String(file.mediaType)}` : ""}]`]
|
|
160
|
+
}
|
|
161
|
+
case "tool-call": {
|
|
162
|
+
const input = cut(JSON.stringify(part.input ?? {}), bounds.toolChars)
|
|
163
|
+
const outcome =
|
|
164
|
+
part.state === "output-error"
|
|
165
|
+
? `error: ${cut(String(part.errorText ?? ""), bounds.toolChars)}`
|
|
166
|
+
: part.state === "output-available"
|
|
167
|
+
? cut(
|
|
168
|
+
typeof part.output === "string" ? part.output : JSON.stringify(part.output),
|
|
169
|
+
bounds.toolChars
|
|
170
|
+
)
|
|
171
|
+
: String(part.state ?? "")
|
|
172
|
+
return [`[${String(part.toolName)}] ${input}`, `→ ${outcome}`]
|
|
173
|
+
}
|
|
174
|
+
case "continuation":
|
|
175
|
+
return ["[asked to continue]"]
|
|
176
|
+
case "approval-outcome":
|
|
177
|
+
return [`[approval ${String(part.status)}]`]
|
|
178
|
+
// Reasoning, provider state and step marks are not part of what was said.
|
|
179
|
+
default:
|
|
180
|
+
return []
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function messagePage(value: unknown): MessagePage {
|
|
185
|
+
const page = record(value)
|
|
186
|
+
if (!page || !Array.isArray(page.messages) || typeof page.total !== "number") {
|
|
187
|
+
throw new CliError(
|
|
188
|
+
{ code: "invalid_api_response", message: "The Sixb API returned an invalid message page." },
|
|
189
|
+
EXIT_API
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
messages: page.messages as StoredMessage[],
|
|
194
|
+
authors: Array.isArray(page.authors) ? (page.authors as MessagePage["authors"]) : [],
|
|
195
|
+
hasMore: page.hasMore === true,
|
|
196
|
+
total: page.total,
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
201
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
202
|
+
? (value as Record<string, unknown>)
|
|
203
|
+
: undefined
|
|
204
|
+
}
|
package/src/commands/metadata.ts
CHANGED
|
@@ -204,6 +204,13 @@ optionally under filters; k bounds the ranked matches and results carry a score.
|
|
|
204
204
|
export const GROUP_HELP = {
|
|
205
205
|
status: "Usage: sixb status",
|
|
206
206
|
context: "Usage: sixb context",
|
|
207
|
+
history: `Usage:
|
|
208
|
+
sixb history [--offset <n>] [--limit <1-${CLI_LIMITS.history.maximum}>]
|
|
209
|
+
sixb history <seq>
|
|
210
|
+
|
|
211
|
+
Prints this conversation's messages as text, oldest first, including those summarized when it
|
|
212
|
+
was compacted: what was said, the files sent, and each tool call with its result. A page holds
|
|
213
|
+
${CLI_LIMITS.history.default} messages by default and cuts long text; \`sixb history <seq>\` prints one message whole.`,
|
|
207
214
|
project: "Usage: sixb project show",
|
|
208
215
|
ontology: `Usage:
|
|
209
216
|
sixb ontology list [--full]
|
package/src/generated/sixb.mjs
CHANGED
|
@@ -167,6 +167,7 @@ var CLI_LIMITS = {
|
|
|
167
167
|
telemetryHistory: { default: 100, maximum: 1000 },
|
|
168
168
|
linkPage: { default: 100, maximum: 1000 },
|
|
169
169
|
users: { default: 20, maximum: 200 },
|
|
170
|
+
history: { default: 20, maximum: 100 },
|
|
170
171
|
datasetVersions: { default: 20, maximum: 100 },
|
|
171
172
|
datasetRows: { default: 100, maximum: 1000 },
|
|
172
173
|
inspect: {
|
|
@@ -372,6 +373,13 @@ optionally under filters; k bounds the ranked matches and results carry a score.
|
|
|
372
373
|
var GROUP_HELP = {
|
|
373
374
|
status: "Usage: sixb status",
|
|
374
375
|
context: "Usage: sixb context",
|
|
376
|
+
history: `Usage:
|
|
377
|
+
sixb history [--offset <n>] [--limit <1-${CLI_LIMITS.history.maximum}>]
|
|
378
|
+
sixb history <seq>
|
|
379
|
+
|
|
380
|
+
Prints this conversation's messages as text, oldest first, including those summarized when it
|
|
381
|
+
was compacted: what was said, the files sent, and each tool call with its result. A page holds
|
|
382
|
+
${CLI_LIMITS.history.default} messages by default and cuts long text; \`sixb history <seq>\` prints one message whole.`,
|
|
375
383
|
project: "Usage: sixb project show",
|
|
376
384
|
ontology: `Usage:
|
|
377
385
|
sixb ontology list [--full]
|
|
@@ -1976,6 +1984,107 @@ async function dispatch(api, command, args) {
|
|
|
1976
1984
|
}
|
|
1977
1985
|
}
|
|
1978
1986
|
|
|
1987
|
+
// src/commands/history.ts
|
|
1988
|
+
var PAGE_TEXT_CHARS = 4000;
|
|
1989
|
+
var PAGE_TOOL_CHARS = 1000;
|
|
1990
|
+
async function history(api, args) {
|
|
1991
|
+
if (isHelp(args[0]) || requestsHelp(args))
|
|
1992
|
+
return writeText(GROUP_HELP.history);
|
|
1993
|
+
const { positionals, options } = parseCommandArgs(args, { "--offset": "string", "--limit": "string" }, "history", [0, 1]);
|
|
1994
|
+
const threadId = process.env.SIXB_THREAD_ID;
|
|
1995
|
+
if (!threadId)
|
|
1996
|
+
fail("This run has no conversation to read: SIXB_THREAD_ID is not set.");
|
|
1997
|
+
const path = `/api/agent-threads/${idSegment(threadId)}/messages`;
|
|
1998
|
+
const seq = positionals[0];
|
|
1999
|
+
if (seq !== undefined) {
|
|
2000
|
+
const wanted = integerInRange("<seq>", seq, 1, Number.MAX_SAFE_INTEGER);
|
|
2001
|
+
const page = messagePage(await api.get(path, { order: "asc", offset: String(wanted - 1), limit: "1" }));
|
|
2002
|
+
const message = page.messages[0];
|
|
2003
|
+
if (message?.seq !== wanted) {
|
|
2004
|
+
throw new CliError({ code: "not_found", message: `The conversation has no message #${wanted}.` }, EXIT_API);
|
|
2005
|
+
}
|
|
2006
|
+
return writeText(renderMessage(message, page, { whole: true }));
|
|
2007
|
+
}
|
|
2008
|
+
const offset = nonNegativeInteger("--offset", options["--offset"] ?? "0");
|
|
2009
|
+
const limit = integerInRange("--limit", options["--limit"] ?? String(CLI_LIMITS.history.default), 1, CLI_LIMITS.history.maximum);
|
|
2010
|
+
const page = messagePage(await api.get(path, { order: "asc", offset: String(offset), limit: String(limit) }));
|
|
2011
|
+
const shown = page.messages.map((message) => renderMessage(message, page, { whole: false }));
|
|
2012
|
+
const end = offset + page.messages.length;
|
|
2013
|
+
const footer = page.messages.length === 0 ? `No messages from offset ${offset}: the conversation has ${page.total}.` : page.hasMore ? `Messages ${offset + 1}–${end} of ${page.total}. Next: sixb history --offset ${end}` : `Messages ${offset + 1}–${end} of ${page.total}: the end of the conversation.`;
|
|
2014
|
+
writeText([...shown, footer].join(`
|
|
2015
|
+
|
|
2016
|
+
`));
|
|
2017
|
+
}
|
|
2018
|
+
function renderMessage(message, page, options) {
|
|
2019
|
+
const author = message.authorPrincipal ? page.authors.find((person) => person.id === message.authorPrincipal?.id)?.displayName : undefined;
|
|
2020
|
+
const heading = [`#${message.seq} ${message.role}`, author, message.createdAt].filter(Boolean).join(" · ");
|
|
2021
|
+
const bounds = {
|
|
2022
|
+
seq: message.seq,
|
|
2023
|
+
textChars: options.whole ? Number.POSITIVE_INFINITY : PAGE_TEXT_CHARS,
|
|
2024
|
+
toolChars: options.whole ? Number.POSITIVE_INFINITY : PAGE_TOOL_CHARS
|
|
2025
|
+
};
|
|
2026
|
+
return [
|
|
2027
|
+
`── ${heading}`,
|
|
2028
|
+
...message.parts.flatMap((part, index) => [
|
|
2029
|
+
...renderPart(part, bounds),
|
|
2030
|
+
...renderOutputFile(part, message, index)
|
|
2031
|
+
])
|
|
2032
|
+
].join(`
|
|
2033
|
+
`);
|
|
2034
|
+
}
|
|
2035
|
+
function renderOutputFile(part, message, index) {
|
|
2036
|
+
const file = part.type === "tool-call" ? record(part.outputFile) : undefined;
|
|
2037
|
+
const fileRef = record(file?.fileRef);
|
|
2038
|
+
if (typeof file?.path !== "string" || typeof fileRef?.sizeBytes !== "number")
|
|
2039
|
+
return [];
|
|
2040
|
+
const contextDir = process.env.SIXB_CONTEXT_DIR;
|
|
2041
|
+
const path = contextDir ? `${contextDir.replace(/\/$/, "")}/${file.path}` : file.path;
|
|
2042
|
+
const apiBaseUrl = process.env.SIXB_API_BASE_URL;
|
|
2043
|
+
const threadId = process.env.SIXB_THREAD_ID;
|
|
2044
|
+
const download = apiBaseUrl && threadId && message.id ? `; else: sixb files download url ${apiBaseUrl.replace(/\/$/, "")}/api/agent-threads/${idSegment(threadId)}/messages/${idSegment(message.id)}/files/content?path=${encodeURIComponent(`/parts/${index}/outputFile/fileRef`)} --output <file>` : "";
|
|
2045
|
+
return [
|
|
2046
|
+
` [whole output, ${fileRef.sizeBytes.toLocaleString("en-US")} bytes: ${path}, written back to the sandbox at each run while the conversation shows this call${download}]`
|
|
2047
|
+
];
|
|
2048
|
+
}
|
|
2049
|
+
function renderPart(part, bounds) {
|
|
2050
|
+
const cut = (value, max) => value.length <= max ? value : `${value.slice(0, max)}… [${(value.length - max).toLocaleString("en-US")} more characters: sixb history ${bounds.seq}]`;
|
|
2051
|
+
switch (part.type) {
|
|
2052
|
+
case "text":
|
|
2053
|
+
return typeof part.text === "string" && part.text.trim() ? [cut(part.text, bounds.textChars)] : [];
|
|
2054
|
+
case "file": {
|
|
2055
|
+
const file = record(part.fileRef);
|
|
2056
|
+
const name = file?.fileName ?? file?.logicalPath ?? file?.blobId;
|
|
2057
|
+
return [`[file ${String(name)}${file?.mediaType ? `, ${String(file.mediaType)}` : ""}]`];
|
|
2058
|
+
}
|
|
2059
|
+
case "tool-call": {
|
|
2060
|
+
const input = cut(JSON.stringify(part.input ?? {}), bounds.toolChars);
|
|
2061
|
+
const outcome = part.state === "output-error" ? `error: ${cut(String(part.errorText ?? ""), bounds.toolChars)}` : part.state === "output-available" ? cut(typeof part.output === "string" ? part.output : JSON.stringify(part.output), bounds.toolChars) : String(part.state ?? "");
|
|
2062
|
+
return [`[${String(part.toolName)}] ${input}`, `→ ${outcome}`];
|
|
2063
|
+
}
|
|
2064
|
+
case "continuation":
|
|
2065
|
+
return ["[asked to continue]"];
|
|
2066
|
+
case "approval-outcome":
|
|
2067
|
+
return [`[approval ${String(part.status)}]`];
|
|
2068
|
+
default:
|
|
2069
|
+
return [];
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
function messagePage(value) {
|
|
2073
|
+
const page = record(value);
|
|
2074
|
+
if (!page || !Array.isArray(page.messages) || typeof page.total !== "number") {
|
|
2075
|
+
throw new CliError({ code: "invalid_api_response", message: "The Sixb API returned an invalid message page." }, EXIT_API);
|
|
2076
|
+
}
|
|
2077
|
+
return {
|
|
2078
|
+
messages: page.messages,
|
|
2079
|
+
authors: Array.isArray(page.authors) ? page.authors : [],
|
|
2080
|
+
hasMore: page.hasMore === true,
|
|
2081
|
+
total: page.total
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
function record(value) {
|
|
2085
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
2086
|
+
}
|
|
2087
|
+
|
|
1979
2088
|
// src/commands/memory.ts
|
|
1980
2089
|
import { lstat, mkdir as mkdir2, readdir, readFile as readFile3, rm as rm2, writeFile } from "node:fs/promises";
|
|
1981
2090
|
import { dirname as dirname3, join as join3 } from "node:path";
|
|
@@ -2529,6 +2638,8 @@ async function main(args) {
|
|
|
2529
2638
|
return context(rest);
|
|
2530
2639
|
if (command === "memory")
|
|
2531
2640
|
return memory(rest, sandboxMode());
|
|
2641
|
+
if (command === "history")
|
|
2642
|
+
return history(createInstanceApiClient(sandboxMode()), rest);
|
|
2532
2643
|
if (!isInstanceCommand(command))
|
|
2533
2644
|
fail(`Unknown command '${command}'. Run 'sixb --help'.`);
|
|
2534
2645
|
await runInstanceCli({ args, mode: sandboxMode() });
|
package/src/main.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// The `sixb` a sandbox runs: the API commands people run, plus the run's own (`status`,
|
|
4
|
-
// `context`, `memory`). `bun run generate:sandbox-cli` bundles it into generated/sixb.mjs.
|
|
4
|
+
// `context`, `memory`, `history`). `bun run generate:sandbox-cli` bundles it into generated/sixb.mjs.
|
|
5
5
|
import { isHelp } from "./arguments"
|
|
6
6
|
import { isInstanceCommand } from "./commands"
|
|
7
|
+
import { history } from "./commands/history"
|
|
7
8
|
import { memory } from "./commands/memory"
|
|
8
9
|
import { renderInstanceHelp } from "./commands/metadata"
|
|
9
10
|
import { context, status } from "./commands/system"
|
|
10
11
|
import { fail, INSTANCE_CLI_VERSION, reportError, writeText } from "./output"
|
|
11
|
-
import { runInstanceCli } from "./run"
|
|
12
|
+
import { createInstanceApiClient, runInstanceCli } from "./run"
|
|
12
13
|
|
|
13
14
|
async function main(args: readonly string[]): Promise<void> {
|
|
14
15
|
const [command, ...rest] = args
|
|
@@ -19,6 +20,7 @@ async function main(args: readonly string[]): Promise<void> {
|
|
|
19
20
|
if (command === "status") return status(rest, sandboxMode())
|
|
20
21
|
if (command === "context") return context(rest)
|
|
21
22
|
if (command === "memory") return memory(rest, sandboxMode())
|
|
23
|
+
if (command === "history") return history(createInstanceApiClient(sandboxMode()), rest)
|
|
22
24
|
if (!isInstanceCommand(command)) fail(`Unknown command '${command}'. Run 'sixb --help'.`)
|
|
23
25
|
|
|
24
26
|
await runInstanceCli({ args, mode: sandboxMode() })
|
package/src/policies.ts
CHANGED
|
@@ -8,6 +8,8 @@ export const CLI_LIMITS = {
|
|
|
8
8
|
linkPage: { default: 100, maximum: 1_000 },
|
|
9
9
|
/** The server lists at most 200 members per page. */
|
|
10
10
|
users: { default: 20, maximum: 200 },
|
|
11
|
+
/** A page of a conversation's messages, as `sixb history` prints them. */
|
|
12
|
+
history: { default: 20, maximum: 100 },
|
|
11
13
|
/** The server's dataset bounds: 100 versions and 1,000 rows per page. */
|
|
12
14
|
datasetVersions: { default: 20, maximum: 100 },
|
|
13
15
|
datasetRows: { default: 100, maximum: 1_000 },
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// What `sandbox-cli.ts` imports as text. Bun's loader reads them; the type checker needs telling.
|
|
2
|
+
declare module "*/bin/sixb" {
|
|
3
|
+
const text: string
|
|
4
|
+
export default text
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module "*/generated/sixb.mjs" {
|
|
8
|
+
const text: string
|
|
9
|
+
export default text
|
|
10
|
+
}
|
package/src/sandbox-cli.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference path="./sandbox-cli-assets.d.ts" />
|
|
2
|
+
import launcher from "./bin/sixb" with { type: "text" }
|
|
3
|
+
import bundle from "./generated/sixb.mjs" with { type: "text" }
|
|
2
4
|
|
|
3
5
|
/** The `sixb` a sandbox installs: a shell launcher and the bundle it runs on Bun or Node. */
|
|
4
6
|
export interface SandboxCliAssets {
|
|
@@ -6,13 +8,9 @@ export interface SandboxCliAssets {
|
|
|
6
8
|
readonly artifact: Uint8Array
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
const assets: SandboxCliAssets = { launcher, artifact: new TextEncoder().encode(bundle) }
|
|
10
12
|
|
|
11
|
-
/**
|
|
12
|
-
export function loadSandboxCliAssets(): Promise<SandboxCliAssets> {
|
|
13
|
-
|
|
14
|
-
readFile(new URL("./bin/sixb", import.meta.url), "utf8"),
|
|
15
|
-
readFile(new URL("./generated/sixb.mjs", import.meta.url)),
|
|
16
|
-
]).then(([launcher, artifact]) => ({ launcher, artifact }))
|
|
17
|
-
return cachedAssets
|
|
13
|
+
/** Part of this module, so that a program compiled to one file carries them too. */
|
|
14
|
+
export async function loadSandboxCliAssets(): Promise<SandboxCliAssets> {
|
|
15
|
+
return assets
|
|
18
16
|
}
|
package/dist/bin/sixb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
|
|
3
|
-
set -eu
|
|
4
|
-
|
|
5
|
-
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
6
|
-
artifact="$script_dir/../lib/sixb.mjs"
|
|
7
|
-
if [ ! -f "$artifact" ]; then
|
|
8
|
-
artifact="$script_dir/../generated/sixb.mjs"
|
|
9
|
-
fi
|
|
10
|
-
if [ ! -f "$artifact" ]; then
|
|
11
|
-
printf '%s\n' '{"error":{"code":"runtime_unavailable","message":"The Sixb CLI artifact is missing."}}' >&2
|
|
12
|
-
exit 2
|
|
13
|
-
fi
|
|
14
|
-
|
|
15
|
-
bun_path=$(command -v bun || true)
|
|
16
|
-
if [ -n "$bun_path" ]; then
|
|
17
|
-
exec "$bun_path" "$artifact" "$@"
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
node_path=$(command -v node || true)
|
|
21
|
-
if [ -n "$node_path" ]; then
|
|
22
|
-
exec "$node_path" "$artifact" "$@"
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
printf '%s\n' '{"error":{"code":"runtime_unavailable","message":"The Sixb CLI requires Bun or Node.js."}}' >&2
|
|
26
|
-
exit 2
|