ticlawk 0.1.16-dev.6 → 0.1.16-dev.7
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/package.json
CHANGED
|
@@ -436,6 +436,20 @@ export async function uploadAgentAttachment({
|
|
|
436
436
|
});
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
+
export async function uploadAgentAvatar({
|
|
440
|
+
actingAgentId, filename, contentType, dataBase64,
|
|
441
|
+
}) {
|
|
442
|
+
return apiFetch('/api/agent/profile/avatar', {
|
|
443
|
+
method: 'POST',
|
|
444
|
+
body: JSON.stringify({
|
|
445
|
+
acting_as_agent_id: actingAgentId,
|
|
446
|
+
filename,
|
|
447
|
+
content_type: contentType,
|
|
448
|
+
data_base64: dataBase64,
|
|
449
|
+
}),
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
439
453
|
export async function viewAgentAttachment({ actingAgentId, assetId }) {
|
|
440
454
|
const params = new URLSearchParams();
|
|
441
455
|
params.set('acting_as_agent_id', actingAgentId);
|
|
@@ -545,13 +545,12 @@ export async function runProfileUpdateCommand(args) {
|
|
|
545
545
|
}
|
|
546
546
|
let avatarUrl = null;
|
|
547
547
|
if (avatarFile) {
|
|
548
|
-
|
|
549
|
-
const upload = await uploadFileViaDaemon(env, avatarFile);
|
|
548
|
+
const upload = await uploadAvatarViaDaemon(env, avatarFile);
|
|
550
549
|
if (!upload.ok) {
|
|
551
550
|
console.error(`avatar upload failed: ${upload.error}`);
|
|
552
551
|
return 1;
|
|
553
552
|
}
|
|
554
|
-
avatarUrl = upload.
|
|
553
|
+
avatarUrl = upload.url;
|
|
555
554
|
}
|
|
556
555
|
const res = await daemonRequest({
|
|
557
556
|
method: 'POST',
|
|
@@ -567,6 +566,34 @@ export async function runProfileUpdateCommand(args) {
|
|
|
567
566
|
return exitFromStatus(res.statusCode);
|
|
568
567
|
}
|
|
569
568
|
|
|
569
|
+
async function uploadAvatarViaDaemon(env, filePath) {
|
|
570
|
+
let stat;
|
|
571
|
+
try { stat = statSync(filePath); } catch (err) {
|
|
572
|
+
return { ok: false, error: `cannot stat ${filePath}: ${err.message}` };
|
|
573
|
+
}
|
|
574
|
+
if (!stat.isFile()) return { ok: false, error: `${filePath} is not a regular file` };
|
|
575
|
+
const contentType = inferContentType(filePath);
|
|
576
|
+
if (!contentType.startsWith('image/')) {
|
|
577
|
+
return { ok: false, error: `avatar must be an image (got content_type ${contentType})` };
|
|
578
|
+
}
|
|
579
|
+
const data = readFileSync(filePath);
|
|
580
|
+
const res = await daemonRequest({
|
|
581
|
+
method: 'POST',
|
|
582
|
+
path: '/agent/profile/avatar',
|
|
583
|
+
headers: commonHeaders(env),
|
|
584
|
+
body: {
|
|
585
|
+
filename: basename(filePath),
|
|
586
|
+
content_type: contentType,
|
|
587
|
+
data_base64: data.toString('base64'),
|
|
588
|
+
},
|
|
589
|
+
});
|
|
590
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
591
|
+
return { ok: false, error: res.body?.error || `HTTP ${res.statusCode}` };
|
|
592
|
+
}
|
|
593
|
+
if (!res.body?.url) return { ok: false, error: 'avatar upload returned no url' };
|
|
594
|
+
return { ok: true, url: res.body.url };
|
|
595
|
+
}
|
|
596
|
+
|
|
570
597
|
async function uploadFileViaDaemon(env, filePath) {
|
|
571
598
|
let stat;
|
|
572
599
|
try { stat = statSync(filePath); } catch (err) {
|
|
@@ -588,7 +615,11 @@ async function uploadFileViaDaemon(env, filePath) {
|
|
|
588
615
|
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
589
616
|
return { ok: false, error: res.body?.error || `HTTP ${res.statusCode}` };
|
|
590
617
|
}
|
|
591
|
-
|
|
618
|
+
const asset = res.body?.data;
|
|
619
|
+
if (!asset?.asset_id || !asset?.url) {
|
|
620
|
+
return { ok: false, error: 'attachment upload returned no asset' };
|
|
621
|
+
}
|
|
622
|
+
return { ok: true, assetId: asset.asset_id, url: asset.url, expiresAt: asset.expires_at };
|
|
592
623
|
}
|
|
593
624
|
|
|
594
625
|
function inferContentType(filePath) {
|
|
@@ -587,8 +587,8 @@ export async function handleAttachmentUpload(req, body, ctx) {
|
|
|
587
587
|
});
|
|
588
588
|
debugLog('agent-cli', 'attachment.upload', {
|
|
589
589
|
actingAgentId,
|
|
590
|
-
asset_id: data?.
|
|
591
|
-
bytes: data?.
|
|
590
|
+
asset_id: data?.data?.asset_id,
|
|
591
|
+
bytes: data?.data?.size_bytes,
|
|
592
592
|
});
|
|
593
593
|
return { status: 200, body: data };
|
|
594
594
|
} catch (err) {
|
|
@@ -596,6 +596,28 @@ export async function handleAttachmentUpload(req, body, ctx) {
|
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
+
export async function handleProfileAvatarUpload(req, body, ctx) {
|
|
600
|
+
const actingAgentId = getActingAgentId(req, body);
|
|
601
|
+
const v = validateActingAgent(actingAgentId, ctx);
|
|
602
|
+
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
603
|
+
if (!body?.data_base64) return { status: 400, body: { error: 'data_base64 is required' } };
|
|
604
|
+
if (!body?.content_type || !String(body.content_type).startsWith('image/')) {
|
|
605
|
+
return { status: 400, body: { error: 'content_type must be image/*' } };
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
const data = await api.uploadAgentAvatar({
|
|
609
|
+
actingAgentId,
|
|
610
|
+
filename: body?.filename || 'avatar.bin',
|
|
611
|
+
contentType: body.content_type,
|
|
612
|
+
dataBase64: body.data_base64,
|
|
613
|
+
});
|
|
614
|
+
debugLog('agent-cli', 'avatar.upload', { actingAgentId, url: data?.url });
|
|
615
|
+
return { status: 200, body: data };
|
|
616
|
+
} catch (err) {
|
|
617
|
+
return { status: err?.status || 500, body: { error: err?.message || 'avatar upload failed' } };
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
599
621
|
export async function handleAttachmentView(req, query, ctx) {
|
|
600
622
|
const actingAgentId = getActingAgentId(req, query);
|
|
601
623
|
const v = validateActingAgent(actingAgentId, ctx);
|
package/src/core/http.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
handleMessageRead,
|
|
12
12
|
handleMessageSearch,
|
|
13
13
|
handleMessageSend,
|
|
14
|
+
handleProfileAvatarUpload,
|
|
14
15
|
handleProfileShow,
|
|
15
16
|
handleProfileUpdate,
|
|
16
17
|
handleReminderCancel,
|
|
@@ -148,6 +149,12 @@ export function startLocalHttpServer({ port, adapter, ctx }) {
|
|
|
148
149
|
const r = await handleProfileUpdate(req, body, cliCtx);
|
|
149
150
|
return writeJson(res, r.status, r.body);
|
|
150
151
|
}
|
|
152
|
+
if (urlNoQuery === '/agent/profile/avatar' && method === 'POST') {
|
|
153
|
+
const body = await readJsonBody(req);
|
|
154
|
+
if (body === null) return writeJson(res, 400, { error: 'invalid json body' });
|
|
155
|
+
const r = await handleProfileAvatarUpload(req, body, cliCtx);
|
|
156
|
+
return writeJson(res, r.status, r.body);
|
|
157
|
+
}
|
|
151
158
|
if (urlNoQuery === '/agent/attachment/upload' && method === 'POST') {
|
|
152
159
|
const body = await readJsonBody(req);
|
|
153
160
|
if (body === null) return writeJson(res, 400, { error: 'invalid json body' });
|