create-walle 0.9.19 → 0.9.21
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/README.md +2 -2
- package/package.json +1 -1
- package/template/claude-task-manager/db.js +131 -0
- package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +58 -50
- package/template/claude-task-manager/docs/phone-access-design.md +23 -7
- package/template/claude-task-manager/docs/walle-session-model-preferences.md +119 -0
- package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +32 -48
- package/template/claude-task-manager/lib/remote-relay-protocol.js +5 -0
- package/template/claude-task-manager/lib/walle-external-actions.js +20 -3
- package/template/claude-task-manager/public/index.html +25 -0
- package/template/claude-task-manager/public/js/setup.js +16 -12
- package/template/claude-task-manager/public/js/walle-session.js +31 -3
- package/template/claude-task-manager/public/js/walle.js +93 -23
- package/template/claude-task-manager/public/m/app.css +417 -21
- package/template/claude-task-manager/public/m/app.js +831 -44
- package/template/claude-task-manager/public/m/claim.html +1 -1
- package/template/claude-task-manager/public/m/index.html +41 -7
- package/template/claude-task-manager/public/m/sw.js +1 -1
- package/template/claude-task-manager/server.js +377 -30
- package/template/claude-task-manager/workers/state-detectors/codex.js +18 -3
- package/template/package.json +1 -1
- package/template/wall-e/chat.js +32 -2
- package/template/wall-e/coding/stream-processor.js +36 -0
- package/template/wall-e/coding-orchestrator.js +45 -0
- package/template/wall-e/deploy.sh +1 -1
- package/template/wall-e/docs/external-action-controller.md +60 -2
- package/template/wall-e/external-action-controller.js +23 -1
- package/template/wall-e/external-action-gateway.js +163 -0
- package/template/wall-e/fly.toml +1 -0
- package/template/wall-e/tools/local-tools.js +122 -4
- package/template/website/index.html +2 -2
|
@@ -686,6 +686,39 @@ async function _fetchGmailMessage(accountRecord, id, includeContent = false) {
|
|
|
686
686
|
], { timeout: includeContent ? 45000 : 30000 });
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
+
async function _verifyGmailMessageSent(accountRecord, gmailMessageId, expected = {}) {
|
|
690
|
+
if (!gmailMessageId) {
|
|
691
|
+
return {
|
|
692
|
+
ok: false,
|
|
693
|
+
source: 'gmail.messages.get',
|
|
694
|
+
reason: 'gmail_send_returned_no_message_id',
|
|
695
|
+
account: accountRecord?.email || null,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
try {
|
|
699
|
+
const msg = await _fetchGmailMessage(accountRecord, gmailMessageId, false);
|
|
700
|
+
const threadOk = expected.threadId ? msg.threadId === expected.threadId : true;
|
|
701
|
+
return {
|
|
702
|
+
ok: threadOk,
|
|
703
|
+
source: 'gmail.messages.get',
|
|
704
|
+
account: accountRecord.email,
|
|
705
|
+
gmailMessageId,
|
|
706
|
+
messageId: _gmailMessageRef(accountRecord.email, gmailMessageId),
|
|
707
|
+
threadId: msg.threadId || null,
|
|
708
|
+
labels: msg.labelIds || [],
|
|
709
|
+
reason: threadOk ? undefined : `sent message threadId ${msg.threadId || '(none)'} did not match expected ${expected.threadId}`,
|
|
710
|
+
};
|
|
711
|
+
} catch (err) {
|
|
712
|
+
return {
|
|
713
|
+
ok: false,
|
|
714
|
+
source: 'gmail.messages.get',
|
|
715
|
+
account: accountRecord?.email || null,
|
|
716
|
+
gmailMessageId,
|
|
717
|
+
error: err.message || String(err),
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
689
722
|
async function _fetchGmailAttachment(accountRecord, messageId, attachmentId) {
|
|
690
723
|
if (!attachmentId) throw new Error('attachment_id is required');
|
|
691
724
|
const url = new URL(`https://gmail.googleapis.com/gmail/v1/users/me/messages/${encodeURIComponent(messageId)}/attachments/${encodeURIComponent(attachmentId)}`);
|
|
@@ -1062,7 +1095,19 @@ async function _tryGwsMailSend(input = {}) {
|
|
|
1062
1095
|
args.push('--body', input.body || '');
|
|
1063
1096
|
args.push('--format', 'json');
|
|
1064
1097
|
const result = await _runGwsGmail(acct, args, { timeout: 45000 });
|
|
1065
|
-
|
|
1098
|
+
const sentId = result?.id || result?.message?.id || result?.messageId || null;
|
|
1099
|
+
const verification = !result?.error
|
|
1100
|
+
? await _verifyGmailMessageSent(acct, sentId)
|
|
1101
|
+
: { ok: false, source: 'gmail.messages.get', reason: 'gmail_send_failed' };
|
|
1102
|
+
return _attachGwsMeta({
|
|
1103
|
+
sent: !result?.error,
|
|
1104
|
+
verified: verification.ok === true,
|
|
1105
|
+
verification,
|
|
1106
|
+
output: result,
|
|
1107
|
+
account: acct.email,
|
|
1108
|
+
gmailMessageId: sentId,
|
|
1109
|
+
messageId: sentId ? _gmailMessageRef(acct.email, sentId) : null,
|
|
1110
|
+
}, [acct], _gwsScopeExtra(scope));
|
|
1066
1111
|
}
|
|
1067
1112
|
|
|
1068
1113
|
async function _tryGwsMailReply(input = {}) {
|
|
@@ -1137,13 +1182,21 @@ async function _tryGwsMailReply(input = {}) {
|
|
|
1137
1182
|
'--format', 'json',
|
|
1138
1183
|
];
|
|
1139
1184
|
const result = await _runGwsGmail(acct, args, { timeout: 45000 });
|
|
1185
|
+
const sentId = result?.id || result?.message?.id || result?.messageId || null;
|
|
1186
|
+
const verification = !result?.error
|
|
1187
|
+
? await _verifyGmailMessageSent(acct, sentId, { threadId: reply.threadId })
|
|
1188
|
+
: { ok: false, source: 'gmail.messages.get', reason: 'gmail_reply_failed' };
|
|
1140
1189
|
return _attachGwsMeta({
|
|
1141
1190
|
sent: !result?.error,
|
|
1191
|
+
verified: verification.ok === true,
|
|
1192
|
+
verification,
|
|
1142
1193
|
reply: true,
|
|
1143
1194
|
output: result,
|
|
1144
1195
|
account: acct.email,
|
|
1145
1196
|
messageId: _gmailMessageRef(acct.email, id),
|
|
1146
1197
|
gmailMessageId: id,
|
|
1198
|
+
sentMessageId: sentId ? _gmailMessageRef(acct.email, sentId) : null,
|
|
1199
|
+
gmailSentMessageId: sentId,
|
|
1147
1200
|
threadId: result?.threadId || reply.threadId,
|
|
1148
1201
|
to: reply.to,
|
|
1149
1202
|
cc: reply.cc,
|
|
@@ -2025,6 +2078,45 @@ function _normalizeGwsCalendar(cal = {}, accountRecord = {}) {
|
|
|
2025
2078
|
};
|
|
2026
2079
|
}
|
|
2027
2080
|
|
|
2081
|
+
async function _verifyGwsCalendarEvent(accountRecord, calendarId, eventId, expected = {}) {
|
|
2082
|
+
if (!calendarId || !eventId) {
|
|
2083
|
+
return {
|
|
2084
|
+
ok: false,
|
|
2085
|
+
source: 'calendar.events.get',
|
|
2086
|
+
account: accountRecord?.email || null,
|
|
2087
|
+
calendarId: calendarId || null,
|
|
2088
|
+
eventId: eventId || null,
|
|
2089
|
+
reason: 'calendar_create_returned_no_event_id',
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
try {
|
|
2093
|
+
const url = new URL(`https://www.googleapis.com/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(eventId)}`);
|
|
2094
|
+
const event = await _googleWorkspaceApiRequest(accountRecord, url, { timeout: 45000 });
|
|
2095
|
+
const titleOk = expected.title ? String(event.summary || '') === String(expected.title) : true;
|
|
2096
|
+
return {
|
|
2097
|
+
ok: titleOk,
|
|
2098
|
+
source: 'calendar.events.get',
|
|
2099
|
+
account: accountRecord.email,
|
|
2100
|
+
calendarId,
|
|
2101
|
+
eventId,
|
|
2102
|
+
htmlLink: event.htmlLink || null,
|
|
2103
|
+
summary: event.summary || '',
|
|
2104
|
+
start: event.start || null,
|
|
2105
|
+
end: event.end || null,
|
|
2106
|
+
reason: titleOk ? undefined : `verified event title "${event.summary || ''}" did not match expected "${expected.title}"`,
|
|
2107
|
+
};
|
|
2108
|
+
} catch (err) {
|
|
2109
|
+
return {
|
|
2110
|
+
ok: false,
|
|
2111
|
+
source: 'calendar.events.get',
|
|
2112
|
+
account: accountRecord?.email || null,
|
|
2113
|
+
calendarId,
|
|
2114
|
+
eventId,
|
|
2115
|
+
error: err.message || String(err),
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2028
2120
|
async function _tryGwsCalendarList(input = {}) {
|
|
2029
2121
|
const source = String(input.source || input.provider || 'auto').toLowerCase();
|
|
2030
2122
|
if (source === 'macos' || source === 'calendar_app' || source === 'apple_calendar') return null;
|
|
@@ -2198,12 +2290,25 @@ async function _tryGwsCalendarCreate(input = {}) {
|
|
|
2198
2290
|
];
|
|
2199
2291
|
if (input.dry_run) args.push('--dry-run');
|
|
2200
2292
|
const result = await _runGws(account, args, { timeout: 45000 });
|
|
2293
|
+
const eventId = result?.id || null;
|
|
2294
|
+
const verification = (!input.dry_run && !result?.error)
|
|
2295
|
+
? await _verifyGwsCalendarEvent(account, match.id, eventId, { title: input.title || '' })
|
|
2296
|
+
: {
|
|
2297
|
+
ok: input.dry_run ? false : false,
|
|
2298
|
+
source: 'calendar.events.get',
|
|
2299
|
+
reason: input.dry_run ? 'dry_run_not_created' : 'calendar_create_failed',
|
|
2300
|
+
account: account.email,
|
|
2301
|
+
calendarId: match.id,
|
|
2302
|
+
eventId,
|
|
2303
|
+
};
|
|
2201
2304
|
return _attachGwsCalendarMeta({
|
|
2202
2305
|
created: input.dry_run ? false : !result?.error,
|
|
2306
|
+
verified: verification.ok === true,
|
|
2307
|
+
verification,
|
|
2203
2308
|
validated: input.dry_run ? !result?.error : undefined,
|
|
2204
2309
|
dry_run: Boolean(input.dry_run),
|
|
2205
|
-
id:
|
|
2206
|
-
uid: result?.iCalUID ||
|
|
2310
|
+
id: eventId,
|
|
2311
|
+
uid: result?.iCalUID || eventId,
|
|
2207
2312
|
htmlLink: result?.htmlLink || null,
|
|
2208
2313
|
output: result,
|
|
2209
2314
|
calendar: { id: match.id, name: match.name, account: match.account, provider: match.provider },
|
|
@@ -2318,7 +2423,20 @@ async function createCalendarEvent(input = {}) {
|
|
|
2318
2423
|
end tell
|
|
2319
2424
|
`;
|
|
2320
2425
|
const result = await runAppleScript(script);
|
|
2321
|
-
return {
|
|
2426
|
+
return {
|
|
2427
|
+
created: true,
|
|
2428
|
+
verified: false,
|
|
2429
|
+
verification: {
|
|
2430
|
+
ok: false,
|
|
2431
|
+
source: 'macos_calendar',
|
|
2432
|
+
reason: 'macos_calendar_create_has_no_read_after_write_verifier',
|
|
2433
|
+
calendar: exact.name,
|
|
2434
|
+
uid: result.output,
|
|
2435
|
+
},
|
|
2436
|
+
uid: result.output,
|
|
2437
|
+
calendar: exact.name,
|
|
2438
|
+
source: 'macos',
|
|
2439
|
+
};
|
|
2322
2440
|
}
|
|
2323
2441
|
|
|
2324
2442
|
async function createReminder({ title, due_date, notes, list }) {
|
|
@@ -241,7 +241,7 @@
|
|
|
241
241
|
<h1>Your AI Coding <span class="accent">Command Center</span> +<br>Personal <span class="accent">Agent</span></h1>
|
|
242
242
|
<p class="sub">
|
|
243
243
|
Run Claude Code, Codex, Gemini, Aider, OpenCode, and Cursor Agent sessions side by side. Manage prompts,
|
|
244
|
-
queue tasks, review code and docs, open a phone-friendly remote UI, and let an AI agent build a second brain from your work life.
|
|
244
|
+
queue tasks, review code and docs, open a phone-friendly remote UI with live prompts and model controls, and let an AI agent build a second brain from your work life.
|
|
245
245
|
</p>
|
|
246
246
|
<div class="install-box" onclick="navigator.clipboard.writeText('npx create-walle install ./walle');this.querySelector('.copy-hint').textContent='Copied!'">
|
|
247
247
|
<code>npx create-walle install ./walle</code>
|
|
@@ -307,7 +307,7 @@
|
|
|
307
307
|
<div class="feature-card">
|
|
308
308
|
<span class="icon">📱</span>
|
|
309
309
|
<h3>Remote Phone Access</h3>
|
|
310
|
-
<p>Pair your phone with a QR code and use a mobile CTM UI through Microsoft Dev Tunnels, Tailscale, Cloudflare Tunnel, or Walle Remote.</p>
|
|
310
|
+
<p>Pair your phone with a QR code and use a mobile CTM UI through Microsoft Dev Tunnels, Tailscale, Cloudflare Tunnel, or Walle Remote, including live prompts and model controls.</p>
|
|
311
311
|
</div>
|
|
312
312
|
<div class="feature-card">
|
|
313
313
|
<span class="icon">Δ</span>
|