@slock-ai/daemon 0.41.1-alpha.0 → 0.43.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.
- package/dist/chat-bridge.js +25 -2
- package/dist/{chunk-JAB3HALZ.js → chunk-37O7EHYE.js} +910 -159
- package/dist/cli/index.js +542 -4
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -159,6 +159,7 @@ var ApiClient = class {
|
|
|
159
159
|
async parseJsonResponse(res) {
|
|
160
160
|
let data = null;
|
|
161
161
|
let error = null;
|
|
162
|
+
let errorCode = null;
|
|
162
163
|
const contentType = res.headers.get("content-type") ?? "";
|
|
163
164
|
if (contentType.includes("application/json")) {
|
|
164
165
|
const parsed = await res.json().catch(() => null);
|
|
@@ -166,11 +167,12 @@ var ApiClient = class {
|
|
|
166
167
|
data = parsed;
|
|
167
168
|
} else {
|
|
168
169
|
error = parsed?.error ?? `HTTP ${res.status}`;
|
|
170
|
+
errorCode = parsed?.errorCode ?? null;
|
|
169
171
|
}
|
|
170
172
|
} else if (!res.ok) {
|
|
171
173
|
error = `HTTP ${res.status}`;
|
|
172
174
|
}
|
|
173
|
-
return { ok: res.ok, status: res.status, data, error };
|
|
175
|
+
return { ok: res.ok, status: res.status, data, error, errorCode };
|
|
174
176
|
}
|
|
175
177
|
async request(method, pathname, body) {
|
|
176
178
|
const url = new URL(pathname, this.ctx.serverUrl).toString();
|
|
@@ -229,11 +231,32 @@ var ApiClient = class {
|
|
|
229
231
|
};
|
|
230
232
|
|
|
231
233
|
// src/commands/server/_format.ts
|
|
234
|
+
function formatRuntimeContext(ctx) {
|
|
235
|
+
if (!ctx) return "";
|
|
236
|
+
const lines = [
|
|
237
|
+
"### Current Runtime",
|
|
238
|
+
"Authoritative context for this agent process. Do not infer computer identity from hostname or cwd when this section is present."
|
|
239
|
+
];
|
|
240
|
+
if (ctx.agentId) lines.push(`- Agent ID: ${ctx.agentId}`);
|
|
241
|
+
if (ctx.serverId) lines.push(`- Server ID: ${ctx.serverId}`);
|
|
242
|
+
if (ctx.machineName || ctx.machineId) {
|
|
243
|
+
const label = ctx.machineName && ctx.machineId ? `${ctx.machineName} (${ctx.machineId})` : ctx.machineName || ctx.machineId;
|
|
244
|
+
lines.push(`- Computer: ${label}`);
|
|
245
|
+
}
|
|
246
|
+
if (ctx.machineHostname) lines.push(`- Hostname: ${ctx.machineHostname}`);
|
|
247
|
+
if (ctx.machineOs) lines.push(`- OS: ${ctx.machineOs}`);
|
|
248
|
+
if (ctx.daemonVersion) lines.push(`- Daemon: v${ctx.daemonVersion}`);
|
|
249
|
+
if (ctx.workspacePath) lines.push(`- Workspace: ${ctx.workspacePath}`);
|
|
250
|
+
return lines.length > 2 ? `${lines.join("\n")}
|
|
251
|
+
|
|
252
|
+
` : "";
|
|
253
|
+
}
|
|
232
254
|
function formatServerInfo(data) {
|
|
233
255
|
let text = "## Server\n\n";
|
|
234
256
|
const channels = data.channels ?? [];
|
|
235
257
|
const agents = data.agents ?? [];
|
|
236
258
|
const humans = data.humans ?? [];
|
|
259
|
+
text += formatRuntimeContext(data.runtimeContext);
|
|
237
260
|
text += "### Channels\n";
|
|
238
261
|
text += 'Visible public channels may appear even when `joined=false`. Use `slock message read --channel "#name"` to inspect them. When a channel is not joined, you cannot send messages there or receive ordinary channel delivery until a human adds you to the channel. To leave a channel you have joined, use `slock channel leave --target "#name"`. To stop following a thread, use `slock thread unfollow --target "#name:shortid"`.\n';
|
|
239
262
|
if (channels.length > 0) {
|
|
@@ -348,7 +371,14 @@ function registerServerInfoCommand(parent) {
|
|
|
348
371
|
const code = res.status >= 500 ? "SERVER_5XX" : "INFO_FAILED";
|
|
349
372
|
fail(code, res.error ?? `HTTP ${res.status}`);
|
|
350
373
|
}
|
|
351
|
-
|
|
374
|
+
const data = res.data;
|
|
375
|
+
if (data?.runtimeContext) {
|
|
376
|
+
data.runtimeContext = {
|
|
377
|
+
...data.runtimeContext,
|
|
378
|
+
workspacePath: data.runtimeContext.workspacePath ?? process.env.SLOCK_CURRENT_WORKSPACE_PATH ?? null
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
process.stdout.write(formatServerInfo(data));
|
|
352
382
|
});
|
|
353
383
|
}
|
|
354
384
|
|
|
@@ -826,11 +856,67 @@ function registerSearchCommand(parent) {
|
|
|
826
856
|
import { existsSync, statSync, readFileSync } from "fs";
|
|
827
857
|
import { basename } from "path";
|
|
828
858
|
var MAX_BYTES = 10 * 1024 * 1024;
|
|
859
|
+
var FILENAME_MIME_MAP = {
|
|
860
|
+
".jpg": "image/jpeg",
|
|
861
|
+
".jpeg": "image/jpeg",
|
|
862
|
+
".png": "image/png",
|
|
863
|
+
".gif": "image/gif",
|
|
864
|
+
".webp": "image/webp",
|
|
865
|
+
".pdf": "application/pdf",
|
|
866
|
+
".txt": "text/plain",
|
|
867
|
+
".md": "text/markdown",
|
|
868
|
+
".json": "application/json",
|
|
869
|
+
".csv": "text/csv"
|
|
870
|
+
};
|
|
871
|
+
var MIME_TYPE_RE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/i;
|
|
872
|
+
var AttachmentUploadArgError = class extends Error {
|
|
873
|
+
constructor(code, message) {
|
|
874
|
+
super(message);
|
|
875
|
+
this.code = code;
|
|
876
|
+
this.name = "AttachmentUploadArgError";
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
function inferMimeTypeFromFilename(filename) {
|
|
880
|
+
const index = filename.lastIndexOf(".");
|
|
881
|
+
const ext = index >= 0 ? filename.slice(index).toLowerCase() : "";
|
|
882
|
+
return FILENAME_MIME_MAP[ext] || null;
|
|
883
|
+
}
|
|
884
|
+
function inferMimeTypeFromBuffer(buffer) {
|
|
885
|
+
if (buffer.length >= 8 && buffer.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
886
|
+
return "image/png";
|
|
887
|
+
}
|
|
888
|
+
if (buffer.length >= 3 && buffer.subarray(0, 3).equals(Buffer.from([255, 216, 255]))) {
|
|
889
|
+
return "image/jpeg";
|
|
890
|
+
}
|
|
891
|
+
if (buffer.length >= 6) {
|
|
892
|
+
const header = buffer.subarray(0, 6).toString("ascii");
|
|
893
|
+
if (header === "GIF87a" || header === "GIF89a") return "image/gif";
|
|
894
|
+
}
|
|
895
|
+
if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
896
|
+
return "image/webp";
|
|
897
|
+
}
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
function normalizeExplicitMimeType(mimeType) {
|
|
901
|
+
const normalized = mimeType?.trim().toLowerCase();
|
|
902
|
+
if (!normalized) return null;
|
|
903
|
+
if (!MIME_TYPE_RE.test(normalized)) {
|
|
904
|
+
throw new AttachmentUploadArgError(
|
|
905
|
+
"INVALID_ARG",
|
|
906
|
+
`--mime-type must look like type/subtype, got: ${mimeType}`
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return normalized;
|
|
910
|
+
}
|
|
911
|
+
function inferUploadMimeType(filename, buffer, explicitMimeType) {
|
|
912
|
+
const explicit = normalizeExplicitMimeType(explicitMimeType);
|
|
913
|
+
return explicit || inferMimeTypeFromBuffer(buffer) || inferMimeTypeFromFilename(filename) || "application/octet-stream";
|
|
914
|
+
}
|
|
829
915
|
function registerAttachmentUploadCommand(parent) {
|
|
830
916
|
parent.command("upload").description("Upload a local file as an attachment (max 10MB)").requiredOption("--path <filepath>", "Absolute path to the local file to upload").option(
|
|
831
917
|
"--channel <target>",
|
|
832
918
|
"Target where the attachment will be used: '#channel', 'dm:@peer', or thread variants. Required by the v0 server until channel-less uploads land."
|
|
833
|
-
).action(async (opts) => {
|
|
919
|
+
).option("--mime-type <type>", "Explicit MIME type override, e.g. image/png").action(async (opts) => {
|
|
834
920
|
let ctx;
|
|
835
921
|
try {
|
|
836
922
|
ctx = loadAgentContext();
|
|
@@ -871,10 +957,21 @@ function registerAttachmentUploadCommand(parent) {
|
|
|
871
957
|
const channelId = resolved.data.channelId;
|
|
872
958
|
const buffer = readFileSync(opts.path);
|
|
873
959
|
const filename = basename(opts.path);
|
|
874
|
-
|
|
960
|
+
let explicitMimeType;
|
|
961
|
+
try {
|
|
962
|
+
explicitMimeType = normalizeExplicitMimeType(opts.mimeType);
|
|
963
|
+
} catch (err) {
|
|
964
|
+
if (err instanceof AttachmentUploadArgError) fail(err.code, err.message);
|
|
965
|
+
throw err;
|
|
966
|
+
}
|
|
967
|
+
const uploadMimeType = inferUploadMimeType(filename, buffer, explicitMimeType);
|
|
968
|
+
const blob = new Blob([buffer], { type: uploadMimeType });
|
|
875
969
|
const form = new FormData();
|
|
876
970
|
form.append("file", blob, filename);
|
|
877
971
|
form.append("channelId", channelId);
|
|
972
|
+
if (explicitMimeType) {
|
|
973
|
+
form.append("mimeType", explicitMimeType);
|
|
974
|
+
}
|
|
878
975
|
const res = await client.requestMultipart(
|
|
879
976
|
"POST",
|
|
880
977
|
`${agentPath}/upload`,
|
|
@@ -1145,6 +1242,444 @@ function registerTaskUpdateCommand(parent) {
|
|
|
1145
1242
|
});
|
|
1146
1243
|
}
|
|
1147
1244
|
|
|
1245
|
+
// ../shared/src/tracing/index.ts
|
|
1246
|
+
var DEFAULT_TRACE_FLAGS = "00";
|
|
1247
|
+
var TRACE_ID_HEX_LENGTH = 32;
|
|
1248
|
+
var SPAN_ID_HEX_LENGTH = 16;
|
|
1249
|
+
var TRACE_FLAGS_HEX_LENGTH = 2;
|
|
1250
|
+
var TRACE_ID_PATTERN = /^[0-9a-f]{32}$/;
|
|
1251
|
+
var SPAN_ID_PATTERN = /^[0-9a-f]{16}$/;
|
|
1252
|
+
var TRACE_FLAGS_PATTERN = /^[0-9a-f]{2}$/;
|
|
1253
|
+
function isTraceId(value) {
|
|
1254
|
+
return TRACE_ID_PATTERN.test(value) && value !== "0".repeat(TRACE_ID_HEX_LENGTH);
|
|
1255
|
+
}
|
|
1256
|
+
function isSpanId(value) {
|
|
1257
|
+
return SPAN_ID_PATTERN.test(value) && value !== "0".repeat(SPAN_ID_HEX_LENGTH);
|
|
1258
|
+
}
|
|
1259
|
+
function isTraceFlags(value) {
|
|
1260
|
+
return TRACE_FLAGS_PATTERN.test(value);
|
|
1261
|
+
}
|
|
1262
|
+
function assertTraceContext(context) {
|
|
1263
|
+
if (!isTraceId(context.traceId)) {
|
|
1264
|
+
throw new Error(`Invalid traceId: expected ${TRACE_ID_HEX_LENGTH} lowercase hex chars`);
|
|
1265
|
+
}
|
|
1266
|
+
if (!isSpanId(context.spanId)) {
|
|
1267
|
+
throw new Error(`Invalid spanId: expected ${SPAN_ID_HEX_LENGTH} lowercase hex chars`);
|
|
1268
|
+
}
|
|
1269
|
+
if (context.parentSpanId !== null && !isSpanId(context.parentSpanId)) {
|
|
1270
|
+
throw new Error(`Invalid parentSpanId: expected null or ${SPAN_ID_HEX_LENGTH} lowercase hex chars`);
|
|
1271
|
+
}
|
|
1272
|
+
if (!isTraceFlags(context.traceFlags)) {
|
|
1273
|
+
throw new Error(`Invalid traceFlags: expected ${TRACE_FLAGS_HEX_LENGTH} lowercase hex chars`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
function createTraceContext({
|
|
1277
|
+
parent = null,
|
|
1278
|
+
traceId,
|
|
1279
|
+
spanId,
|
|
1280
|
+
traceFlags,
|
|
1281
|
+
traceIdGenerator = generateTraceId,
|
|
1282
|
+
spanIdGenerator = generateSpanId
|
|
1283
|
+
} = {}) {
|
|
1284
|
+
const context = {
|
|
1285
|
+
traceId: traceId ?? parent?.traceId ?? traceIdGenerator(),
|
|
1286
|
+
spanId: spanId ?? spanIdGenerator(),
|
|
1287
|
+
parentSpanId: parent?.spanId ?? null,
|
|
1288
|
+
traceFlags: traceFlags ?? parent?.traceFlags ?? DEFAULT_TRACE_FLAGS
|
|
1289
|
+
};
|
|
1290
|
+
assertTraceContext(context);
|
|
1291
|
+
return context;
|
|
1292
|
+
}
|
|
1293
|
+
var NoopTracer = class {
|
|
1294
|
+
startSpan(_name, options) {
|
|
1295
|
+
return new NoopActiveSpan(createTraceContext({ parent: options.parent ?? null }));
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
var NoopActiveSpan = class {
|
|
1299
|
+
context;
|
|
1300
|
+
constructor(context) {
|
|
1301
|
+
this.context = context;
|
|
1302
|
+
}
|
|
1303
|
+
addEvent() {
|
|
1304
|
+
}
|
|
1305
|
+
end() {
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
var noopTracer = new NoopTracer();
|
|
1309
|
+
function generateTraceId() {
|
|
1310
|
+
return randomNonZeroHex(TRACE_ID_HEX_LENGTH);
|
|
1311
|
+
}
|
|
1312
|
+
function generateSpanId() {
|
|
1313
|
+
return randomNonZeroHex(SPAN_ID_HEX_LENGTH);
|
|
1314
|
+
}
|
|
1315
|
+
function randomNonZeroHex(length) {
|
|
1316
|
+
let value = randomHex(length);
|
|
1317
|
+
while (value === "0".repeat(length)) {
|
|
1318
|
+
value = randomHex(length);
|
|
1319
|
+
}
|
|
1320
|
+
return value;
|
|
1321
|
+
}
|
|
1322
|
+
function randomHex(length) {
|
|
1323
|
+
const bytes = new Uint8Array(length / 2);
|
|
1324
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
1325
|
+
return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// ../shared/src/testing/failpoints.ts
|
|
1329
|
+
var NoopFailpointRegistry = class {
|
|
1330
|
+
get enabled() {
|
|
1331
|
+
return false;
|
|
1332
|
+
}
|
|
1333
|
+
isEnabled() {
|
|
1334
|
+
return false;
|
|
1335
|
+
}
|
|
1336
|
+
configure() {
|
|
1337
|
+
}
|
|
1338
|
+
clear() {
|
|
1339
|
+
}
|
|
1340
|
+
getTrace() {
|
|
1341
|
+
return [];
|
|
1342
|
+
}
|
|
1343
|
+
hit(_key, _context, fallback) {
|
|
1344
|
+
return fallback ? fallback() : void 0;
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
var noopFailpointRegistry = new NoopFailpointRegistry();
|
|
1348
|
+
|
|
1349
|
+
// ../shared/src/serverPermissions.ts
|
|
1350
|
+
var EMPTY_SERVER_CAPABILITIES = Object.freeze({
|
|
1351
|
+
manageServer: false,
|
|
1352
|
+
manageChannels: false,
|
|
1353
|
+
manageAgents: false,
|
|
1354
|
+
manageMachines: false,
|
|
1355
|
+
manageMembers: false,
|
|
1356
|
+
changeMemberRoles: false,
|
|
1357
|
+
manageBilling: false,
|
|
1358
|
+
joinPublicChannels: false
|
|
1359
|
+
});
|
|
1360
|
+
var SERVER_CAPABILITY_MATRIX = {
|
|
1361
|
+
owner: Object.freeze({
|
|
1362
|
+
manageServer: true,
|
|
1363
|
+
manageChannels: true,
|
|
1364
|
+
manageAgents: true,
|
|
1365
|
+
manageMachines: true,
|
|
1366
|
+
manageMembers: true,
|
|
1367
|
+
changeMemberRoles: true,
|
|
1368
|
+
manageBilling: true,
|
|
1369
|
+
joinPublicChannels: true
|
|
1370
|
+
}),
|
|
1371
|
+
admin: Object.freeze({
|
|
1372
|
+
manageServer: true,
|
|
1373
|
+
manageChannels: true,
|
|
1374
|
+
manageAgents: true,
|
|
1375
|
+
manageMachines: true,
|
|
1376
|
+
manageMembers: true,
|
|
1377
|
+
changeMemberRoles: true,
|
|
1378
|
+
manageBilling: false,
|
|
1379
|
+
joinPublicChannels: true
|
|
1380
|
+
}),
|
|
1381
|
+
member: Object.freeze({
|
|
1382
|
+
manageServer: false,
|
|
1383
|
+
manageChannels: false,
|
|
1384
|
+
manageAgents: false,
|
|
1385
|
+
manageMachines: false,
|
|
1386
|
+
manageMembers: false,
|
|
1387
|
+
changeMemberRoles: false,
|
|
1388
|
+
manageBilling: false,
|
|
1389
|
+
joinPublicChannels: true
|
|
1390
|
+
})
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
// ../shared/src/index.ts
|
|
1394
|
+
var RUNTIMES = [
|
|
1395
|
+
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
1396
|
+
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
1397
|
+
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
1398
|
+
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
1399
|
+
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
1400
|
+
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
|
|
1401
|
+
{ id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true }
|
|
1402
|
+
];
|
|
1403
|
+
function getRuntimeDisplayName(id) {
|
|
1404
|
+
return RUNTIMES.find((r) => r.id === id)?.displayName ?? id;
|
|
1405
|
+
}
|
|
1406
|
+
var PLAN_CONFIG = {
|
|
1407
|
+
free: {
|
|
1408
|
+
displayName: "Hobby",
|
|
1409
|
+
limits: { maxMachines: 2, maxAgents: 5, maxChannels: 5, messageHistoryDays: 30, includedAgents: 5 },
|
|
1410
|
+
comingSoon: false,
|
|
1411
|
+
price: 0,
|
|
1412
|
+
extraAgentPrice: 0
|
|
1413
|
+
},
|
|
1414
|
+
founder: {
|
|
1415
|
+
displayName: "Founder",
|
|
1416
|
+
limits: { maxMachines: -1, maxAgents: -1, maxChannels: -1, messageHistoryDays: -1, includedAgents: -1 },
|
|
1417
|
+
comingSoon: false,
|
|
1418
|
+
price: 0,
|
|
1419
|
+
extraAgentPrice: 0
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
var DISPLAY_PLAN_CONFIG = {
|
|
1423
|
+
free: PLAN_CONFIG.free,
|
|
1424
|
+
pro: {
|
|
1425
|
+
displayName: "Team",
|
|
1426
|
+
limits: { maxMachines: 8, maxAgents: 40, maxChannels: 20, messageHistoryDays: -1, includedAgents: 40 },
|
|
1427
|
+
comingSoon: true,
|
|
1428
|
+
price: 20,
|
|
1429
|
+
extraAgentPrice: 0
|
|
1430
|
+
},
|
|
1431
|
+
max: {
|
|
1432
|
+
displayName: "Business",
|
|
1433
|
+
limits: { maxMachines: 40, maxAgents: 200, maxChannels: -1, messageHistoryDays: -1, includedAgents: 200 },
|
|
1434
|
+
comingSoon: true,
|
|
1435
|
+
price: 200,
|
|
1436
|
+
extraAgentPrice: 0
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
// src/commands/profile/_format.ts
|
|
1441
|
+
function formatCreatedAgents(createdAgents) {
|
|
1442
|
+
if (createdAgents.length === 0) {
|
|
1443
|
+
return ["- Created Agents: none"];
|
|
1444
|
+
}
|
|
1445
|
+
return [
|
|
1446
|
+
`- Created Agents (${createdAgents.length}):`,
|
|
1447
|
+
...createdAgents.map((createdAgent) => ` - @${createdAgent.name} (${getRuntimeDisplayName(createdAgent.runtime)}, ${createdAgent.status})`)
|
|
1448
|
+
];
|
|
1449
|
+
}
|
|
1450
|
+
function formatHumanProfile(profile) {
|
|
1451
|
+
const lines = [
|
|
1452
|
+
"## Profile",
|
|
1453
|
+
"",
|
|
1454
|
+
"- Type: human",
|
|
1455
|
+
`- Handle: @${profile.name}`,
|
|
1456
|
+
`- Display Name: ${profile.displayName ?? "(none)"}`,
|
|
1457
|
+
`- Description: ${profile.description ?? "(none)"}`,
|
|
1458
|
+
`- Membership: ${profile.membershipStatus}`
|
|
1459
|
+
];
|
|
1460
|
+
if (profile.role) lines.push(`- Role: ${profile.role}`);
|
|
1461
|
+
if (profile.joinedAt) lines.push(`- Joined: ${profile.joinedAt}`);
|
|
1462
|
+
if (profile.email) lines.push(`- Email: ${profile.email}`);
|
|
1463
|
+
return [...lines, ...formatCreatedAgents(profile.createdAgents)].join("\n");
|
|
1464
|
+
}
|
|
1465
|
+
function formatCreator(profile) {
|
|
1466
|
+
if (!profile.creator) return null;
|
|
1467
|
+
return profile.creator.displayName ? `${profile.creator.displayName} (@${profile.creator.name})` : `@${profile.creator.name}`;
|
|
1468
|
+
}
|
|
1469
|
+
function formatAgentProfile(profile) {
|
|
1470
|
+
const lines = [
|
|
1471
|
+
"## Profile",
|
|
1472
|
+
"",
|
|
1473
|
+
"- Type: agent",
|
|
1474
|
+
`- Handle: @${profile.name}`,
|
|
1475
|
+
`- Display Name: ${profile.displayName ?? "(none)"}`,
|
|
1476
|
+
`- Description: ${profile.description ?? "(none)"}`,
|
|
1477
|
+
`- Status: ${profile.status}`,
|
|
1478
|
+
`- Runtime: ${getRuntimeDisplayName(profile.runtime)}`,
|
|
1479
|
+
`- Model: ${profile.model}`,
|
|
1480
|
+
`- Reasoning: ${profile.reasoningEffort ?? "medium"}`
|
|
1481
|
+
];
|
|
1482
|
+
if (profile.executionMode) lines.push(`- Execution: ${profile.executionMode}`);
|
|
1483
|
+
if (profile.computerName || profile.computerId) {
|
|
1484
|
+
const label = profile.computerName && profile.computerId ? `${profile.computerName} (${profile.computerId})` : profile.computerName ?? profile.computerId;
|
|
1485
|
+
lines.push(`- Computer: ${label}`);
|
|
1486
|
+
}
|
|
1487
|
+
if (profile.computerHostname) lines.push(`- Hostname: ${profile.computerHostname}`);
|
|
1488
|
+
if (profile.daemonVersion) lines.push(`- Daemon: v${profile.daemonVersion}`);
|
|
1489
|
+
lines.push(`- Created: ${profile.createdAt}`);
|
|
1490
|
+
if (profile.deletedAt) lines.push(`- Deleted At: ${profile.deletedAt}`);
|
|
1491
|
+
const creator = formatCreator(profile);
|
|
1492
|
+
if (creator) lines.push(`- Creator: ${creator}`);
|
|
1493
|
+
return [...lines, ...formatCreatedAgents(profile.createdAgents)].join("\n");
|
|
1494
|
+
}
|
|
1495
|
+
function formatProfile(profile) {
|
|
1496
|
+
return profile.kind === "human" ? formatHumanProfile(profile) : formatAgentProfile(profile);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// src/commands/profile/show.ts
|
|
1500
|
+
function normalizeTarget(target) {
|
|
1501
|
+
if (target === void 0) return null;
|
|
1502
|
+
const trimmed = target.trim();
|
|
1503
|
+
if (!trimmed) {
|
|
1504
|
+
fail("INVALID_ARG", "profile target must not be empty");
|
|
1505
|
+
}
|
|
1506
|
+
if (!trimmed.startsWith("@")) {
|
|
1507
|
+
fail("INVALID_ARG", "profile target must start with @");
|
|
1508
|
+
}
|
|
1509
|
+
return trimmed;
|
|
1510
|
+
}
|
|
1511
|
+
function registerProfileShowCommand(parent) {
|
|
1512
|
+
parent.command("show").description("Show a profile. Omit the target to show your own profile.").argument("[target]", "Handle like @alice; omit to show your own profile").option("--json", "Emit machine-readable JSON").action(async (target, opts) => {
|
|
1513
|
+
let ctx;
|
|
1514
|
+
try {
|
|
1515
|
+
ctx = loadAgentContext();
|
|
1516
|
+
} catch (err) {
|
|
1517
|
+
if (err instanceof AgentBootstrapError) fail(err.code, err.message);
|
|
1518
|
+
throw err;
|
|
1519
|
+
}
|
|
1520
|
+
const normalizedTarget = normalizeTarget(target);
|
|
1521
|
+
const params = new URLSearchParams();
|
|
1522
|
+
if (normalizedTarget) params.set("target", normalizedTarget);
|
|
1523
|
+
const client = new ApiClient(ctx);
|
|
1524
|
+
const pathname = params.size > 0 ? `/internal/agent/${encodeURIComponent(ctx.agentId)}/profile?${params.toString()}` : `/internal/agent/${encodeURIComponent(ctx.agentId)}/profile`;
|
|
1525
|
+
const res = await client.request("GET", pathname);
|
|
1526
|
+
if (!res.ok || !res.data) {
|
|
1527
|
+
const code = res.status >= 500 ? "SERVER_5XX" : "PROFILE_SHOW_FAILED";
|
|
1528
|
+
fail(code, res.error ?? `HTTP ${res.status}`);
|
|
1529
|
+
}
|
|
1530
|
+
if (opts.json) {
|
|
1531
|
+
emit({ ok: true, data: res.data });
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
process.stdout.write(`${formatProfile(res.data)}
|
|
1535
|
+
`);
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
// src/commands/profile/update.ts
|
|
1540
|
+
import { basename as basename2 } from "path";
|
|
1541
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
1542
|
+
var MAX_PROFILE_AVATAR_BYTES = 2 * 1024 * 1024;
|
|
1543
|
+
var PROFILE_AVATAR_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
1544
|
+
"image/jpeg",
|
|
1545
|
+
"image/png",
|
|
1546
|
+
"image/gif",
|
|
1547
|
+
"image/webp"
|
|
1548
|
+
]);
|
|
1549
|
+
var FILENAME_MIME_MAP2 = {
|
|
1550
|
+
".jpg": "image/jpeg",
|
|
1551
|
+
".jpeg": "image/jpeg",
|
|
1552
|
+
".png": "image/png",
|
|
1553
|
+
".gif": "image/gif",
|
|
1554
|
+
".webp": "image/webp"
|
|
1555
|
+
};
|
|
1556
|
+
var MAX_PROFILE_DESCRIPTION_LENGTH = 3e3;
|
|
1557
|
+
var MAX_PROFILE_DISPLAY_NAME_LENGTH = 80;
|
|
1558
|
+
function inferImageMimeType(filename, buffer) {
|
|
1559
|
+
const lowerFilename = filename.toLowerCase();
|
|
1560
|
+
if (buffer.length >= 8 && buffer.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
1561
|
+
return "image/png";
|
|
1562
|
+
}
|
|
1563
|
+
if (buffer.length >= 3 && buffer.subarray(0, 3).equals(Buffer.from([255, 216, 255]))) {
|
|
1564
|
+
return "image/jpeg";
|
|
1565
|
+
}
|
|
1566
|
+
if (buffer.length >= 6) {
|
|
1567
|
+
const header = buffer.subarray(0, 6).toString("ascii");
|
|
1568
|
+
if (header === "GIF87a" || header === "GIF89a") return "image/gif";
|
|
1569
|
+
}
|
|
1570
|
+
if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
1571
|
+
return "image/webp";
|
|
1572
|
+
}
|
|
1573
|
+
const dot = lowerFilename.lastIndexOf(".");
|
|
1574
|
+
return dot >= 0 ? FILENAME_MIME_MAP2[lowerFilename.slice(dot)] ?? null : null;
|
|
1575
|
+
}
|
|
1576
|
+
function readAvatarFile(avatarFile) {
|
|
1577
|
+
if (!existsSync2(avatarFile)) {
|
|
1578
|
+
fail("PROFILE_FILE_NOT_FOUND", `Avatar file does not exist: ${avatarFile}`);
|
|
1579
|
+
}
|
|
1580
|
+
const stat = statSync2(avatarFile);
|
|
1581
|
+
if (!stat.isFile()) {
|
|
1582
|
+
fail("PROFILE_FILE_NOT_FOUND", `Avatar file is not a regular file: ${avatarFile}`);
|
|
1583
|
+
}
|
|
1584
|
+
if (stat.size > MAX_PROFILE_AVATAR_BYTES) {
|
|
1585
|
+
fail(
|
|
1586
|
+
"PROFILE_AVATAR_TOO_LARGE",
|
|
1587
|
+
`Avatar file is ${stat.size} bytes; max size is ${MAX_PROFILE_AVATAR_BYTES} bytes`
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
const buffer = readFileSync2(avatarFile);
|
|
1591
|
+
const filename = basename2(avatarFile);
|
|
1592
|
+
const mimeType = inferImageMimeType(filename, buffer);
|
|
1593
|
+
if (!mimeType || !PROFILE_AVATAR_MIME_TYPES.has(mimeType)) {
|
|
1594
|
+
fail(
|
|
1595
|
+
"PROFILE_AVATAR_BAD_FORMAT",
|
|
1596
|
+
"Avatar must be a JPEG, PNG, GIF, or WebP image"
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
return { filename, buffer, mimeType };
|
|
1600
|
+
}
|
|
1601
|
+
function registerProfileUpdateCommand(parent) {
|
|
1602
|
+
parent.command("update").description("Update your own profile").option("--avatar-file <path>", "Path to a local image file to use as your avatar").option("--display-name <name>", "Set your display name (non-empty)").option("--description <text>", "Set your profile description (non-empty)").option("--json", "Emit machine-readable JSON").action(async (opts) => {
|
|
1603
|
+
let ctx;
|
|
1604
|
+
try {
|
|
1605
|
+
ctx = loadAgentContext();
|
|
1606
|
+
} catch (err) {
|
|
1607
|
+
if (err instanceof AgentBootstrapError) fail(err.code, err.message);
|
|
1608
|
+
throw err;
|
|
1609
|
+
}
|
|
1610
|
+
const hasAvatar = opts.avatarFile !== void 0;
|
|
1611
|
+
const hasDisplayName = opts.displayName !== void 0;
|
|
1612
|
+
const hasDescription = opts.description !== void 0;
|
|
1613
|
+
if (!hasAvatar && !hasDisplayName && !hasDescription) {
|
|
1614
|
+
fail("INVALID_ARG", "Provide at least one of --avatar-file, --display-name, or --description");
|
|
1615
|
+
}
|
|
1616
|
+
let trimmedDisplayName;
|
|
1617
|
+
if (hasDisplayName) {
|
|
1618
|
+
trimmedDisplayName = opts.displayName.trim();
|
|
1619
|
+
if (trimmedDisplayName.length === 0) {
|
|
1620
|
+
fail("INVALID_ARG", "--display-name must not be empty");
|
|
1621
|
+
}
|
|
1622
|
+
if (trimmedDisplayName.length > MAX_PROFILE_DISPLAY_NAME_LENGTH) {
|
|
1623
|
+
fail("INVALID_ARG", `--display-name must be at most ${MAX_PROFILE_DISPLAY_NAME_LENGTH} characters`);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (hasDescription) {
|
|
1627
|
+
if (opts.description.length === 0) {
|
|
1628
|
+
fail("INVALID_ARG", "--description must not be empty");
|
|
1629
|
+
}
|
|
1630
|
+
if (opts.description.length > MAX_PROFILE_DESCRIPTION_LENGTH) {
|
|
1631
|
+
fail("INVALID_ARG", `--description must be at most ${MAX_PROFILE_DESCRIPTION_LENGTH} characters`);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
const client = new ApiClient(ctx);
|
|
1635
|
+
let latestProfile = null;
|
|
1636
|
+
if (hasDisplayName || hasDescription) {
|
|
1637
|
+
const body = {};
|
|
1638
|
+
if (hasDisplayName) {
|
|
1639
|
+
body.displayName = trimmedDisplayName;
|
|
1640
|
+
}
|
|
1641
|
+
if (hasDescription) {
|
|
1642
|
+
body.description = opts.description;
|
|
1643
|
+
}
|
|
1644
|
+
const res = await client.request(
|
|
1645
|
+
"POST",
|
|
1646
|
+
`/internal/agent/${encodeURIComponent(ctx.agentId)}/profile`,
|
|
1647
|
+
body
|
|
1648
|
+
);
|
|
1649
|
+
if (!res.ok || !res.data) {
|
|
1650
|
+
const code = res.errorCode ?? (res.status >= 500 ? "SERVER_5XX" : "PROFILE_UPDATE_FAILED");
|
|
1651
|
+
fail(code, res.error ?? `HTTP ${res.status}`);
|
|
1652
|
+
}
|
|
1653
|
+
latestProfile = res.data;
|
|
1654
|
+
}
|
|
1655
|
+
if (hasAvatar) {
|
|
1656
|
+
const avatar = readAvatarFile(opts.avatarFile);
|
|
1657
|
+
const form = new FormData();
|
|
1658
|
+
const avatarBytes = Uint8Array.from(avatar.buffer);
|
|
1659
|
+
form.append("avatar", new Blob([avatarBytes], { type: avatar.mimeType }), avatar.filename);
|
|
1660
|
+
const res = await client.requestMultipart(
|
|
1661
|
+
"POST",
|
|
1662
|
+
`/internal/agent/${encodeURIComponent(ctx.agentId)}/profile/avatar`,
|
|
1663
|
+
form
|
|
1664
|
+
);
|
|
1665
|
+
if (!res.ok || !res.data) {
|
|
1666
|
+
const code = res.errorCode ?? (res.status >= 500 ? "SERVER_5XX" : "PROFILE_UPDATE_FAILED");
|
|
1667
|
+
fail(code, res.error ?? `HTTP ${res.status}`);
|
|
1668
|
+
}
|
|
1669
|
+
latestProfile = res.data;
|
|
1670
|
+
}
|
|
1671
|
+
if (!latestProfile) {
|
|
1672
|
+
fail("PROFILE_UPDATE_FAILED", "No profile returned from server");
|
|
1673
|
+
}
|
|
1674
|
+
if (opts.json) {
|
|
1675
|
+
emit({ ok: true, data: latestProfile });
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
process.stdout.write(`${formatProfile(latestProfile)}
|
|
1679
|
+
`);
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1148
1683
|
// src/commands/reminder/_format.ts
|
|
1149
1684
|
function toLocalTime2(iso) {
|
|
1150
1685
|
const d = new Date(iso);
|
|
@@ -1369,6 +1904,9 @@ registerTaskCreateCommand(taskCmd);
|
|
|
1369
1904
|
registerTaskClaimCommand(taskCmd);
|
|
1370
1905
|
registerTaskUnclaimCommand(taskCmd);
|
|
1371
1906
|
registerTaskUpdateCommand(taskCmd);
|
|
1907
|
+
var profileCmd = program.command("profile").description("Profile operations");
|
|
1908
|
+
registerProfileShowCommand(profileCmd);
|
|
1909
|
+
registerProfileUpdateCommand(profileCmd);
|
|
1372
1910
|
var reminderCmd = program.command("reminder").description("Reminder operations");
|
|
1373
1911
|
registerReminderScheduleCommand(reminderCmd);
|
|
1374
1912
|
registerReminderListCommand(reminderCmd);
|
package/dist/core.js
CHANGED
package/dist/index.js
CHANGED