@rubytech/create-maxy-code 0.1.472 → 0.1.474
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/__tests__/google-client-id-env.test.js +15 -12
- package/dist/index.js +6 -29
- package/dist/port-resolution.js +5 -5
- package/package.json +1 -1
- package/payload/platform/config/brand.json +1 -0
- package/payload/platform/docs/superpowers/plans/2026-07-18-ledger-write-path-fix.md +109 -0
- package/payload/platform/lib/ledger-core/dist/reconcile.d.ts +7 -0
- package/payload/platform/lib/ledger-core/dist/reconcile.d.ts.map +1 -1
- package/payload/platform/lib/ledger-core/dist/reconcile.js +5 -2
- package/payload/platform/lib/ledger-core/dist/reconcile.js.map +1 -1
- package/payload/platform/lib/ledger-core/src/__tests__/reconcile.test.ts +20 -0
- package/payload/platform/lib/ledger-core/src/reconcile.ts +12 -2
- package/payload/platform/neo4j/schema.cypher +4 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +37 -14
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/google-guide.md +20 -9
- package/payload/platform/plugins/docs/references/ledger.md +9 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +4 -0
- package/payload/platform/plugins/docs/references/voice-mirror-guide.md +2 -2
- package/payload/platform/plugins/google/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/google/PLUGIN.md +28 -14
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js +79 -28
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/__tests__/boot.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/boot.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/boot.test.js +54 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/boot.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js +26 -1
- package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js +3 -2
- package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/index.d.ts +9 -5
- package/payload/platform/plugins/google/mcp/dist/index.d.ts.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/index.js +38 -37
- package/payload/platform/plugins/google/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts +10 -7
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.js +25 -13
- package/payload/platform/plugins/google/mcp/dist/lib/google-client.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts +58 -17
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js +143 -37
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js.map +1 -1
- package/payload/platform/plugins/google/references/auth.md +264 -362
- package/payload/platform/plugins/ledger/PLUGIN.md +13 -1
- package/payload/platform/plugins/ledger/mcp/dist/index.js +23 -15
- package/payload/platform/plugins/ledger/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts +11 -1
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.js +20 -1
- package/payload/platform/plugins/ledger/mcp/dist/lib/read.js.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts +14 -11
- package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/lib/types.js.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/lib/write-rejects.d.ts +12 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/write-rejects.d.ts.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/write-rejects.js +50 -0
- package/payload/platform/plugins/ledger/mcp/dist/lib/write-rejects.js.map +1 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts +5 -5
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js +11 -11
- package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts +7 -2
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js +9 -3
- package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts +7 -0
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js +3 -4
- package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts.map +1 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js +4 -1
- package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/PLUGIN.md +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/index.js +3 -3
- package/payload/platform/plugins/voice-mirror/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/voice-corpus.d.ts +14 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/voice-corpus.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/voice-corpus.js +15 -0
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/voice-corpus.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js +3 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/scripts/smoke.mjs +51 -4
- package/payload/platform/plugins/voice-mirror/skills/voice-mirror/SKILL.md +4 -0
- package/payload/platform/scripts/backfill-task-ids.sh +206 -0
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +27 -2
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js +33 -2
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +3 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-BSTIQX52.js → chunk-Y6V45R6M.js} +1 -12
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +6 -6
- package/payload/server/public/assets/{AdminLoginScreens-BfamGqS7.js → AdminLoginScreens-B44KbEuC.js} +1 -1
- package/payload/server/public/assets/{AdminShell-5nHOAzXO.js → AdminShell-BjxV2qkb.js} +2 -2
- package/payload/server/public/assets/{Checkbox-CD6aHwac.js → Checkbox-DSeZNGNA.js} +1 -1
- package/payload/server/public/assets/activity-CdipMTDp.js +1 -0
- package/payload/server/public/assets/{admin-Cpcw_YE-.js → admin-B4LpTCur.js} +1 -1
- package/payload/server/public/assets/{browser-0znOwk1W.js → browser-CEFvaWwI.js} +1 -1
- package/payload/server/public/assets/{calendar-CykigHdg.js → calendar-B3zo6V34.js} +1 -1
- package/payload/server/public/assets/chat-5tJdo9i_.js +1 -0
- package/payload/server/public/assets/chevron-left-BJllrPBj.js +1 -0
- package/payload/server/public/assets/data-Du79EgR-.js +1 -0
- package/payload/server/public/assets/{graph-JV5_xF3V.js → graph-RaOrnSf2.js} +1 -1
- package/payload/server/public/assets/{graph-labels-DPYORjBH.js → graph-labels-GlTEIYJj.js} +1 -1
- package/payload/server/public/assets/{maximize-2-8vKr3h21.js → maximize-2-CHDa8p5h.js} +1 -1
- package/payload/server/public/assets/{operator-Liul1QEq.js → operator-DtqHY4jV.js} +1 -1
- package/payload/server/public/assets/{page-CYRA87CB.js → page-CkhFj8GQ.js} +1 -1
- package/payload/server/public/assets/{page-Cg7B1RQx.js → page-DxnWgPtd.js} +1 -1
- package/payload/server/public/assets/{public-B4HMtpjt.js → public-D5TUfQ3I.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-sz5IQSnr.js → rotate-ccw-BJjVAQQY.js} +1 -1
- package/payload/server/public/assets/tasks-CM9pYYG1.js +1 -0
- package/payload/server/public/assets/{time-entry-format-CSuCexpM.js → time-entry-format-C5RYEYjM.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-ClS81-CU.js → triangle-alert-DLfIsuo_.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-Cn6YgeNx.js → useCopyFeedback-7yKeiAzV.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-BaD-ZZRk.js → useSelectionMode-CWlh6AEq.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-CNZL3PKn.css → useSubAccountSwitcher-B2n9QPVI.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-BOzHrDAU.js → useVoiceRecorder-j3ZArZ2H.js} +1 -1
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +11 -11
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/tasks.html +5 -5
- package/payload/server/server.js +1837 -1695
- package/payload/server/public/assets/activity-Dj2nJzPw.js +0 -1
- package/payload/server/public/assets/chat-DIOJZ411.js +0 -1
- package/payload/server/public/assets/chevron-left-Cq7UvOI0.js +0 -1
- package/payload/server/public/assets/data-lHgqlhmu.js +0 -1
- package/payload/server/public/assets/tasks-D9oYgEzs.js +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-CvjWJSGv.js → useSubAccountSwitcher-CBlKgwms.js} +0 -0
|
@@ -56,7 +56,7 @@ either is a regression.
|
|
|
56
56
|
|---|---|---|
|
|
57
57
|
| `/session` | Admin cookie session: PIN-gated mint, validate, rotate. | `GET /`, `POST /` |
|
|
58
58
|
| `/sessions` | Legacy admin-server conversation routes. No UI consumer remains after the ConversationsModal was retired; the surviving handlers are deletion candidates and not described here. | (legacy, no live caller) |
|
|
59
|
-
| `/sidebar-sessions` | Sole data path for the sidebar Sessions list. One JSONL on disk equals one row. The row's delete button is the only way a row disappears. Each row carries `sessionId`, `title`, `startedAt`, `live`, `isSubagent`, `pid: number \| null` (basename of the matched `sessions/<pid>.json`), and `projectDir` (the directory holding the JSONL — consumed by the delete route). The payload also carries top-level `accountId` so the pane renders the full UUID label whose first ~8 chars prefix-match the truncated Remote Control daemon entry in claude.ai/code. The legacy `rcUrl` field is gone — the row's external-link affordance now POSTs `/session-rc-spawn` to start a fresh local `claude --remote-control <name> --session-id <sid>` PTY on every click. **WhatsApp and Telegram sessions (admin + public) appear in this list**, colour-coded by channel (WhatsApp green `#25D366`, Telegram blue `#229ED9`, via a `conv-channel-*` left accent bar) and carrying the standard row kebab (Usage & cost, Reset/re-seat, Rename, Archive, Delete) so the operator can inspect and re-seat a channel session's params. Each row's `channel` is the authoritative `.meta.json` sidecar value (`whatsapp`/`telegram`/`webchat`/`browser`/null), not the JSONL-envelope channel. Each row also carries its sidecar `senderId`. For a WhatsApp/Telegram row with a `senderId` and no operator rename or ai-title (`titleSource:'prefix'`), the route resolves a contact `personName` and the sidebar renders the reader panel's `"<name> <number>"` label via the shared `operatorConvName`, degrading to the number alone then the stripped title on a miss, so a Sessions row and that conversation's reader-panel row read identically.
|
|
59
|
+
| `/sidebar-sessions` | Sole data path for the sidebar Sessions list. One JSONL on disk equals one row. The row's delete button is the only way a row disappears. Each row carries `sessionId`, `title`, `startedAt`, `live`, `isSubagent`, `pid: number \| null` (basename of the matched `sessions/<pid>.json`), and `projectDir` (the directory holding the JSONL — consumed by the delete route). The payload also carries top-level `accountId` so the pane renders the full UUID label whose first ~8 chars prefix-match the truncated Remote Control daemon entry in claude.ai/code. The legacy `rcUrl` field is gone — the row's external-link affordance now POSTs `/session-rc-spawn` to start a fresh local `claude --remote-control <name> --session-id <sid>` PTY on every click. **WhatsApp and Telegram sessions (admin + public) appear in this list**, colour-coded by channel (WhatsApp green `#25D366`, Telegram blue `#229ED9`, via a `conv-channel-*` left accent bar) and carrying the standard row kebab (Usage & cost, Reset/re-seat, Rename, Archive, Delete) so the operator can inspect and re-seat a channel session's params. Each row's `channel` is the authoritative `.meta.json` sidecar value (`whatsapp`/`telegram`/`webchat`/`browser`/null), not the JSONL-envelope channel. Each row also carries its sidecar `senderId`. For a WhatsApp/Telegram row with a `senderId` and no operator rename or ai-title (`titleSource:'prefix'`), the route resolves a contact `personName` and the sidebar renders the reader panel's `"<name> <number>"` label via the shared `operatorConvName`, degrading to the number alone then the stripped title on a miss, so a Sessions row and that conversation's reader-panel row read identically. A channel row name has two sources, in precedence order. First the shared `resolveOperatorNamesBySender` helper the WhatsApp reader also calls, which matches the sender phone against the account's admins — that names an admin WhatsApp session and every Telegram session. Second, for a `whatsapp` row only, the WhatsApp pushName from the account's on-disk message store (`storePushNamesBySender` over `readConversationSummaries`, keyed by `normalizeE164` of the counterparty phone so a `+44 7…` sender matches a `447…` store key). The store is consulted only when the resolver reports **no binding** for that sender. That is typically a public WhatsApp customer, the case this exists for — such a sender has no admin record and so can only be named by the store, and without it those rows degraded to the bare number while the reader panel named them — though an admin missing from `users.json` qualifies too. A resolver miss caused by Neo4j being unreachable deliberately does NOT qualify: that is an outage rather than an absent binding, so the row degrades to the number and still logs `reason=neo4j-down`, keeping the outage signal intact. Telegram has no message store and is never looked up. The store read is guarded: a corrupt or absent store degrades to no pushName and logs `op=store-unreadable`, never 500s the list. Both sources are the ones the reader itself uses, so the two surfaces cannot drift; a renamed or ai-titled channel row keeps its own title. The Sessions list reads the viewed account's own store only. An account-manager's thread persists under the house account rather than the sub-account it manages, so on a multi-account install viewing a sub-account that row is not named here even though the reader panel names it — the reader additionally consults the house store, filtered to manager phones bound to that sub. The only role/channel excluded from the list is `public`+`webchat` (its dedicated reader is the sole surface); the exclusion predicate is `isSessionListExcluded`, decoupled from the reader's inclusion predicate `isReaderChannelSession` (unchanged), so the WhatsApp/Telegram reader panels are unaffected — a store-only WhatsApp conversation with no session JSONL stays reader-only. Observability rides `[admin-sessions-list] … channelInList=whatsapp:<n>,telegram:<n>,webchat:<n> channelNameCandidates=<n> channelNamesResolved=<n> (operator=<n> store=<n>) unknownChannel=<n> excludedPublicWebchat=<n>`, and each unresolved channel name logs one `[admin-sessions-list] op=channel-name-unresolved sessionId=<id8> channel=<whatsapp\|telegram> senderId=<id> reason=<no-senderId\|classify-miss\|neo4j-down>` line — emitted only when **both** sources miss, so a residual shortfall means a genuinely nameless sender rather than the expected public-customer floor, and the `operator=`/`store=` split says which source is carrying the names. | `GET /` |
|
|
60
60
|
| `/session-delete` | POST `{ sessionId }`. Thin proxy over the manager: POST `/:id/stop` (idempotent) then DELETE `/:id`. Sends **no** accountId. The manager resolves the session by scanning every slug under `projects/` for its JSONL (`slugForExistingJsonl`, covering `archive/`), exactly as the sidebar list enumerates rows, and gates on file existence alone — so any session the operator can see is deletable. This replaces the earlier accountId-derived slug and the `!row` term, which narrowed resolution below the list and left a session under a non-house slug with no watcher row (e.g. a legacy `-home-admin` session on a single-account install) listed yet un-deletable. Account scope is a view filter, not a delete gate — admin access is install-wide, so there is nothing to scope against. The sibling `/session-stop` (the standalone End control) resolves identically and also forwards no accountId. | `POST /` |
|
|
61
61
|
| `/session-rc-spawn` | POST `{ sessionId?, name? }`. Fire-and-forget `claude --remote-control [name] [--session-id <sid>]`. Present `sessionId` resumes; absent starts a fresh session (also used by the sidebar's "New session" button — it no longer opens claude.ai/code directly). Proxies to the manager's `/rc-spawn`, which waits up to **60 s** (raised from 12 s) for the spawned PTY to bind and returns `{ spawnedPid, sessionId, bridgeSessionId, slug, outcome, reason }`. For a webchat-bound spawn (every admin-gated host's "New session", returning a same-origin `/chat?session=<id>` target — `resolveRcSpawnOutcome` → `sameOrigin:true`) the Sidebar navigates the **current** tab via `window.location.assign`, replacing the dashboard in place (back returns to it); only a claude.ai/code slug (`sameOrigin:false`, the bare-admin resume bridge, never a new-session outcome) navigates a separately-opened tab. On `timeout` or `spawn-failed` it shows an error modal (reason + sessionId) and **never** opens a bare claude.ai/code tab. The new process registers itself as its own Remote Control entry in claude.ai/code. | `POST /` |
|
|
62
62
|
| `/claude-sessions` | **Spawn surface only**. `POST /` is the Sidebar new-session-with-prompt path, cookie-auth only (the recorder loopback caller was removed; LinkedIn ingest moved to `/rc-spawn`). The former UI-facing handlers (SSE row feed, list, resume, stop, rename, archive, delete, `/:id/meta`, `/:id/input`, `/:id/log`) were removed — the maxy dashboard no longer manages or displays sessions. | `POST /` |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Google Calendar
|
|
2
2
|
|
|
3
3
|
Live Google Calendar for one account: read the user's real events, and create,
|
|
4
|
-
update, cancel, respond to and check availability against them.
|
|
4
|
+
update, cancel, respond to and check availability against them. Twelve tools,
|
|
5
5
|
admin agent only.
|
|
6
6
|
|
|
7
7
|
There is deliberately no Gmail surface here. Mail runs on the `email` plugin's
|
|
@@ -12,9 +12,16 @@ force Google's annual third-party security assessment. Calendar is only a
|
|
|
12
12
|
## Connecting an account
|
|
13
13
|
|
|
14
14
|
`google-account-register` returns a consent URL. Relay it to the user: they open
|
|
15
|
-
it on any device, sign in, and approve.
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
it on any device, sign in, and approve.
|
|
16
|
+
|
|
17
|
+
Their browser then lands on a page that **fails to load**. That is expected, not
|
|
18
|
+
a fault — the vendor app is a Desktop client, so Google redirects to a loopback
|
|
19
|
+
address on the user's own device where nothing is listening. The address bar
|
|
20
|
+
carries the code. Ask them to copy it and pass it to
|
|
21
|
+
`google-account-register-submit`, which completes the connection.
|
|
22
|
+
|
|
23
|
+
If they paste the whole `code=...` fragment, or a value containing `%2F`, the
|
|
24
|
+
tool handles it. `google-account-register-poll` reports the outcome at any point.
|
|
18
25
|
|
|
19
26
|
The register tool does not block and does not wait. A second register while one
|
|
20
27
|
is already live is refused rather than overwriting it; that refusal names how
|
|
@@ -50,9 +57,12 @@ granted.
|
|
|
50
57
|
|
|
51
58
|
## When something is wrong
|
|
52
59
|
|
|
53
|
-
**"GOOGLE_CLIENT_ID is not configured"
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
**"GOOGLE_CLIENT_ID is not configured"**, or the same for
|
|
61
|
+
`GOOGLE_CLIENT_SECRET` — this brand has no Google vendor app yet. Nothing the
|
|
62
|
+
operator does on the device fixes it, and no file placed on the device fixes it
|
|
63
|
+
either: the id and secret ship in the brand's `brand.json` and reach the device
|
|
64
|
+
through an installer publish. The brand needs its own Google Cloud Desktop OAuth
|
|
65
|
+
client. See `platform/plugins/google/references/auth.md`.
|
|
56
66
|
|
|
57
67
|
**A 403 naming insufficient scope** — the connected account approved something
|
|
58
68
|
narrower than the full calendar scope. Re-run `google-account-register` so they
|
|
@@ -64,5 +74,6 @@ Testing, where Google caps refresh tokens at seven days and limits the app to
|
|
|
64
74
|
property of the vendor app, not a fault in the connection.
|
|
65
75
|
|
|
66
76
|
**A registration never completes** — the consent URL was opened but not
|
|
67
|
-
approved, or the
|
|
68
|
-
|
|
77
|
+
approved, or the code was never copied back to
|
|
78
|
+
`google-account-register-submit`. `google-account-register-poll` reports
|
|
79
|
+
`expired` once the pending entry outlives Google's code lifetime.
|
|
@@ -24,6 +24,12 @@ If you need statutory accounts, this is not that. It is the answer to "who owes
|
|
|
24
24
|
|
|
25
25
|
**Ask for a statement.** Everything owed to you and everything you owe, sorted into 0-30, 31-60, 61-90 and 90+ days overdue, plus net cash.
|
|
26
26
|
|
|
27
|
+
## You look the customer up first
|
|
28
|
+
|
|
29
|
+
When you record an invoice or a cash movement, the agent finds the company or person in your records first, then attaches the money to that exact record. It will not create a new company for you, and the name that appears on the invoice is taken from the record it attached to, so the two can never disagree.
|
|
30
|
+
|
|
31
|
+
That is on purpose. Deciding whether "Beagle Direct Ltd" is the same firm as an existing "Beagle Direct" is a judgement about your records, not a bookkeeping step, and getting it wrong quietly splits one customer's payment history across two entries.
|
|
32
|
+
|
|
27
33
|
## Three behaviours worth knowing about
|
|
28
34
|
|
|
29
35
|
**What is outstanding is never stored.** It is worked out from the payments each time you ask. This means the figure cannot fall out of step with the payments behind it. If it were stored, a payment recorded by any route that forgot to update it would leave the invoice quietly reporting a wrong number, and nothing would fail.
|
|
@@ -36,7 +42,9 @@ If you need statutory accounts, this is not that. It is the answer to "who owes
|
|
|
36
42
|
|
|
37
43
|
Some ways this can go wrong announce themselves. Others do not: a payment that ends up attached to no invoice, a stale balance field reappearing, or the same payment recorded twice under two different ids. None of those produce an error at the time, because nothing goes wrong at the moment they happen.
|
|
38
44
|
|
|
39
|
-
So there is a check that runs by itself every fifteen minutes and writes one line to the log beginning `[ledger-census]`. When it is healthy the counters are all zero. When something is wrong the line ends with `alarm=true` and says which of
|
|
45
|
+
So there is a check that runs by itself every fifteen minutes and writes one line to the log beginning `[ledger-census]`. When it is healthy the counters are all zero. When something is wrong the line ends with `alarm=true` and says which of them it found.
|
|
46
|
+
|
|
47
|
+
One of those counters is the number of writes that were refused in the last day. It is there because of a real failure: for a whole day every attempt to record money was refused, and the check still read as healthy, because a ledger nobody has used and a ledger that cannot be written to both show nothing.
|
|
40
48
|
|
|
41
49
|
You can also ask for that check on demand at any time, and it reports the same thing in plain words.
|
|
42
50
|
|
|
@@ -147,6 +147,10 @@ To deliberately disconnect the wrong account, use the header menu's **Disconnect
|
|
|
147
147
|
3. `grep '\[auth-health\]' ~/.${brand}/logs/server.log | tail -n 5` — the heartbeat fires every five minutes. `status=dead expiresIn=...` means the refresh token is gone; only a re-login fixes it. `status=ok` heartbeats with no spawns in between mean the credentials file is healthy and the failure lives elsewhere.
|
|
148
148
|
4. The spawn-failure surface now carries `reason=auth-refresh-failed` (with `authStatus` in the JSON body) instead of generic `pid-file-timeout` whenever the credentials file is in `dead` or `expired` state at the moment of failure — visible in `grep '\[spawn-failed\]'` on server.log.
|
|
149
149
|
|
|
150
|
+
**A WhatsApp message you can see on your phone never appears in the app.** First check whether it was ever stored, rather than assuming it routed elsewhere: ask {{productName}} for the most recent ingest reconcile, or `grep '\[wa-ingest-audit\]' ~/.${brand}/logs/server.log | tail -1`. A healthy line reads `op=reconcile … unpersisted=0 inFlight=0 guard=installed`. If `unpersisted` is above zero, the `ids=` field names the exact message ids that arrived at the wire and were never written to the store — those messages are genuinely lost to the platform, not hidden in another workspace or sub-account, and they cannot be re-fetched because WhatsApp only delivers an acked message once. Forward the message from your phone to get it back in; it re-enters as a new message through the normal path.
|
|
151
|
+
|
|
152
|
+
The audit runs every 15 minutes and holds a stanza for 10 minutes before judging it, so a message can take up to 25 minutes to be reported. The wait is deliberate: a large batch of photos arriving at once can take several minutes to finish storing, and a false "a message was lost" alarm would make the real one easy to ignore. Three classes are resolved rather than reported, each counted separately: `skippedStatus` (status broadcasts, never stored), `skippedReaction` (reactions, stored under the message they react to rather than under their own id — a consequence being that a lost *reaction* is not detectable), and `declined` (messages carrying neither text nor a file, such as a shared contact card, a poll, or a delete-for-everyone, which are dropped by design). `oldestUnpersistedMin` tells you whether a reported loss is new or one you have already seen, since the same id is re-reported every 15 minutes for a day. `guard=absent` or `inFlight` stuck above zero on an idle install are both faults worth surfacing to support: the first means the media-download crash guard failed to install at boot, the second means its counter leaked. `contained` counts crashes the guard prevented — a number that climbs while media traffic stays flat is also worth reporting.
|
|
153
|
+
|
|
150
154
|
---
|
|
151
155
|
|
|
152
156
|
## Memory Not Working
|
|
@@ -30,7 +30,7 @@ In the admin chat, ask: **"Start the voice-mirror backfill."**
|
|
|
30
30
|
{{productName}} asks which stream to backfill first — discrete documents (emails, posts, PDFs) or chat archives (WhatsApp, Telegram, etc.). **Pick chat archives first if you have any imported.** Chat is where your unguarded voice lives; email is the same voice in dress clothes.
|
|
31
31
|
|
|
32
32
|
- **Chat archives** — tag a whole conversation in one click. {{productName}} doesn't ask about individual messages because chunks within a conversation almost always share the same voice. Options per conversation: yours, mixed (multiple authors on your side — rare), or not yours (e.g. a Slack channel where you only forwarded other people's messages).
|
|
33
|
-
- **Documents and posts** — paginated 10 at a time. {{productName}} shows each item with its
|
|
33
|
+
- **Documents and posts** — paginated 10 at a time. {{productName}} shows each item with its source, date, recipient or topic, and a one-line preview. You name the format on every command — {{productName}} never guesses it. Reach for the broad buckets, `formal` or `informal`, unless a narrower one (`email`, `article`, `note`, `social-post`, `marketing-copy`, `text`) genuinely fits. Tag the whole batch, a subset by number, or per-item if you want a precise label like `human-led-agent-assisted` (you wrote the content, {{productName}} polished).
|
|
34
34
|
|
|
35
35
|
Skip a batch if none qualify, or stop at any time — the next session resumes where you left off.
|
|
36
36
|
|
|
@@ -70,4 +70,4 @@ Voice mirror is on by default for every drafting skill. To opt out for one, set
|
|
|
70
70
|
|
|
71
71
|
## Status
|
|
72
72
|
|
|
73
|
-
Voice mirror is live end-to-end.
|
|
73
|
+
Voice mirror is live end-to-end. Eight corpus formats (the broad buckets `formal` and `informal`, plus the narrower `text`, `email`, `social-post`, `article`, `note`, `marketing-copy`), five tools (`voice-tag-content`, `voice-distil-profile`, `voice-retrieve-conditioning`, `voice-record-feedback`, `voice-ingest-session-text`), on-demand session-turn capture, and wiring into the three live drafting surfaces (email composition, property brochures, investor data room) are all live. When no voice profile exists on the account for a given format, every drafting skill degrades gracefully — the output matches what it was before voice mirror was installed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "google",
|
|
3
|
-
"description": "Live Google Calendar for one account via the Google Calendar API v3 — read plus control. Per-account OAuth authorization-code + PKCE
|
|
3
|
+
"description": "Live Google Calendar for one account via the Google Calendar API v3 — read plus control. Per-account OAuth authorization-code + PKCE against a Desktop client whose id and secret ship in brand config; the plugin mints the consent URL and exchanges the code in-process, and nothing in platform/ui participates. One account can connect several Google accounts; every operational tool takes an optional account selector (the email address) and refuses ambiguously when two or more are connected and none is named. There is deliberately no Gmail surface — mail runs on the `email` plugin's IMAP/SMTP path, and Gmail's restricted scopes would force Google's annual third-party security assessment, whereas Calendar is only a sensitive scope. Tools — google-account-register / google-account-register-submit / google-account-register-poll: connect a Google Calendar (returns a consent URL; the user approves, lands on a page that fails to load by design, and pastes the code back to submit); google-calendar-list: events in the next rangeDays days, recurring series expanded; google-calendar-event: one event by id; google-calendar-create / google-calendar-update / google-calendar-cancel: control events, attendees notified, update changes only the fields named; google-calendar-respond: accept, decline or tentatively accept an invitation, leaving other attendees' responses untouched; google-calendar-freebusy: busy blocks for this or named calendars; google-account-info: auth state for one connected Google account; google-account-list: every connected Google account with its own status.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: google
|
|
3
|
-
description: Live Google Calendar for one account via the Google Calendar API v3 — read plus control. Per-account OAuth authorization-code + PKCE
|
|
3
|
+
description: Live Google Calendar for one account via the Google Calendar API v3 — read plus control. Per-account OAuth authorization-code + PKCE against a Desktop client whose id and secret ship in brand config; the plugin mints the consent URL and exchanges the code in-process, and nothing in platform/ui participates. One account can connect several Google accounts; every operational tool takes an optional account selector (the email address) and refuses ambiguously when two or more are connected and none is named. There is deliberately no Gmail surface — mail runs on the `email` plugin's IMAP/SMTP path, and Gmail's restricted scopes would force Google's annual third-party security assessment, whereas Calendar is only a sensitive scope. Tools — google-account-register / google-account-register-submit / google-account-register-poll: connect a Google Calendar (returns a consent URL; the user approves, lands on a page that fails to load by design, and pastes the code back to submit); google-calendar-list: events in the next rangeDays days, recurring series expanded; google-calendar-event: one event by id; google-calendar-create / google-calendar-update / google-calendar-cancel: control events, attendees notified, update changes only the fields named; google-calendar-respond: accept, decline or tentatively accept an invitation, leaving other attendees' responses untouched; google-calendar-freebusy: busy blocks for this or named calendars; google-account-info: auth state for one connected Google account; google-account-list: every connected Google account with its own status.
|
|
4
4
|
tools:
|
|
5
5
|
- name: google-account-register
|
|
6
6
|
publicAllowlist: false
|
|
@@ -8,6 +8,9 @@ tools:
|
|
|
8
8
|
- name: google-account-register-poll
|
|
9
9
|
publicAllowlist: false
|
|
10
10
|
adminAllowlist: false
|
|
11
|
+
- name: google-account-register-submit
|
|
12
|
+
publicAllowlist: false
|
|
13
|
+
adminAllowlist: false
|
|
11
14
|
- name: google-calendar-list
|
|
12
15
|
publicAllowlist: false
|
|
13
16
|
adminAllowlist: false
|
|
@@ -47,8 +50,7 @@ mcp:
|
|
|
47
50
|
ACCOUNT_ID: ${ACCOUNT_ID}
|
|
48
51
|
SESSION_ID: ${SESSION_ID}
|
|
49
52
|
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
|
|
50
|
-
|
|
51
|
-
MAXY_UI_INTERNAL_PORT: ${MAXY_UI_INTERNAL_PORT}
|
|
53
|
+
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
|
|
52
54
|
mcp-manifest: auto
|
|
53
55
|
---
|
|
54
56
|
|
|
@@ -72,16 +74,23 @@ Authorization-code + PKCE, not device-code. Google's device flow serves a fixed
|
|
|
72
74
|
scope allowlist that excludes Calendar, and the out-of-band paste-a-code flow
|
|
73
75
|
has been blocked since 31 January 2023, so consent must land on a real redirect.
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
install's public host, which verifies `state`, exchanges the code, resolves the
|
|
79
|
-
account's email from its primary calendar, and writes encrypted tokens.
|
|
80
|
-
`google-account-register-poll` then reports the outcome from local state.
|
|
77
|
+
The vendor app is a **Desktop (installed app)** client, the one type whose
|
|
78
|
+
secret Google sanctions shipping in distributed software. Its id and secret come
|
|
79
|
+
from `brand.json` and are stamped by the installer together or not at all.
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
`google-account-register` mints the PKCE pair, persists it single-slot per
|
|
82
|
+
account, and returns a consent URL whose redirect is the IP literal
|
|
83
|
+
`http://127.0.0.1:8571` — never `localhost`, since Google's port-agnostic
|
|
84
|
+
loopback matching applies only to the literal. The user opens the URL on any
|
|
85
|
+
device and approves, then lands on a page that **fails to load by design**
|
|
86
|
+
(nothing listens there) and copies the `code` from the address bar.
|
|
87
|
+
`google-account-register-submit` exchanges it in-process with the stored
|
|
88
|
+
verifier, resolves the account's email from its primary calendar, and writes
|
|
89
|
+
encrypted tokens. `google-account-register-poll` reports the outcome from local
|
|
90
|
+
state.
|
|
91
|
+
|
|
92
|
+
Token refresh also runs in-process against Google. Nothing in `platform/ui`
|
|
93
|
+
participates in Google OAuth.
|
|
85
94
|
|
|
86
95
|
## Scope
|
|
87
96
|
|
|
@@ -94,8 +103,13 @@ requesting `openid`/`email`/`profile`, so consent stays minimal.
|
|
|
94
103
|
| Variable | Source | Effect when unset |
|
|
95
104
|
|---|---|---|
|
|
96
105
|
| `GOOGLE_CLIENT_ID` | `brand.json#googleClientId` | Plugin inert; register refuses with an actionable message |
|
|
97
|
-
| `
|
|
98
|
-
|
|
106
|
+
| `GOOGLE_CLIENT_SECRET` | `brand.json#googleClientSecret` | Plugin inert; register refuses. Stamped with `GOOGLE_CLIENT_ID` or not at all |
|
|
107
|
+
|
|
108
|
+
Both reach this process by env inheritance from the session-manager unit, which
|
|
109
|
+
is why they are declared in the self-named `${FOO}` form (see
|
|
110
|
+
`mcp-descriptor-resolve.ts`). Boot reports `clientIdConfigured` and
|
|
111
|
+
`clientSecretConfigured`; either reading `false` on a brand that ships both is
|
|
112
|
+
the mis-stamp signature.
|
|
99
113
|
|
|
100
114
|
## Out of scope
|
|
101
115
|
|
|
@@ -4,8 +4,9 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { mkdtempSync } from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
-
import { newPkce, buildConsentUrl, runAccountRegisterStart, runAccountRegisterPoll, listConnected, CALENDAR_SCOPE,
|
|
7
|
+
import { newPkce, buildConsentUrl, runAccountRegisterStart, runAccountRegisterPoll, listConnected, CALENDAR_SCOPE, LOOPBACK_REDIRECT_URI, normalisePastedCode, runAccountRegisterSubmit, } from "../tools/account-register.js";
|
|
8
8
|
import { TokenStore } from "../auth/token-store.js";
|
|
9
|
+
import { PendingStore } from "../auth/pending-store.js";
|
|
9
10
|
function newDir() {
|
|
10
11
|
return mkdtempSync(join(tmpdir(), "gcal-r-"));
|
|
11
12
|
}
|
|
@@ -17,7 +18,6 @@ test("PKCE challenge is the base64url SHA-256 of the verifier", () => {
|
|
|
17
18
|
test("consent URL carries offline access, forced consent, and the calendar scope", () => {
|
|
18
19
|
const url = new URL(buildConsentUrl({
|
|
19
20
|
clientId: "cid",
|
|
20
|
-
redirectUri: "https://h/api/google/oauth/callback",
|
|
21
21
|
state: "st",
|
|
22
22
|
challenge: "ch",
|
|
23
23
|
accountId: "acct1",
|
|
@@ -30,13 +30,12 @@ test("consent URL carries offline access, forced consent, and the calendar scope
|
|
|
30
30
|
// NO query string: Google matches redirect_uri exactly against the registered
|
|
31
31
|
// value, so a per-request parameter here fails every request with
|
|
32
32
|
// redirect_uri_mismatch. The account rides in `state` instead.
|
|
33
|
-
assert.equal(url.searchParams.get("redirect_uri"),
|
|
33
|
+
assert.equal(url.searchParams.get("redirect_uri"), LOOPBACK_REDIRECT_URI);
|
|
34
34
|
});
|
|
35
35
|
test("redirect_uri carries no query string at all", () => {
|
|
36
36
|
const url = new URL(buildConsentUrl({
|
|
37
37
|
clientId: "cid",
|
|
38
|
-
|
|
39
|
-
state: encodeState("acct1", "nonce"),
|
|
38
|
+
state: "nonce",
|
|
40
39
|
challenge: "ch",
|
|
41
40
|
accountId: "acct1",
|
|
42
41
|
}));
|
|
@@ -44,33 +43,17 @@ test("redirect_uri carries no query string at all", () => {
|
|
|
44
43
|
assert.ok(!redirect.includes("?"), `redirect_uri must have no query: ${redirect}`);
|
|
45
44
|
assert.ok(!redirect.includes("account="), "the account must not ride on redirect_uri");
|
|
46
45
|
});
|
|
47
|
-
test("
|
|
48
|
-
const st = encodeState("2078cb54-08e9-49e9-bf8e-e9f3ad76ca41", "deadbeef");
|
|
49
|
-
assert.deepEqual(decodeState(st), {
|
|
50
|
-
accountId: "2078cb54-08e9-49e9-bf8e-e9f3ad76ca41",
|
|
51
|
-
nonce: "deadbeef",
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
test("decodeState rejects malformed values", () => {
|
|
55
|
-
assert.equal(decodeState("nodot"), null);
|
|
56
|
-
assert.equal(decodeState(".leading"), null);
|
|
57
|
-
assert.equal(decodeState("trailing."), null);
|
|
58
|
-
});
|
|
59
|
-
test("register stores the nonce, and the consent state carries account plus nonce", async () => {
|
|
46
|
+
test("register stores the same opaque state it sends, and no account id", async () => {
|
|
60
47
|
const dir = newDir();
|
|
61
|
-
const res = await runAccountRegisterStart({
|
|
62
|
-
accountId: "acct1", accountsDir: dir, clientId: "cid", publicBase: "https://h",
|
|
63
|
-
});
|
|
48
|
+
const res = await runAccountRegisterStart({ accountId: "acct1", accountsDir: dir, clientId: "cid" });
|
|
64
49
|
const sent = new URL(res.consentUrl).searchParams.get("state");
|
|
65
|
-
const
|
|
66
|
-
assert.equal(
|
|
67
|
-
|
|
68
|
-
const { PendingStore } = await import("../auth/pending-store.js");
|
|
69
|
-
assert.equal(new PendingStore("acct1", dir).read()?.state, parts.nonce);
|
|
50
|
+
const { PendingStore: PS } = await import("../auth/pending-store.js");
|
|
51
|
+
assert.equal(new PS("acct1", dir).read()?.state, sent, "pending must hold exactly what was sent");
|
|
52
|
+
assert.ok(!sent.includes("acct1"), "the account id must not ride in state");
|
|
70
53
|
});
|
|
71
54
|
test("a second register while one is live is refused as in-progress", async () => {
|
|
72
55
|
const dir = newDir();
|
|
73
|
-
const args = { accountId: "acct1", accountsDir: dir, clientId: "cid"
|
|
56
|
+
const args = { accountId: "acct1", accountsDir: dir, clientId: "cid" };
|
|
74
57
|
const first = await runAccountRegisterStart(args);
|
|
75
58
|
assert.equal(first.status, "pending");
|
|
76
59
|
assert.ok(first.consentUrl);
|
|
@@ -84,7 +67,7 @@ test("poll reports none with nothing connected and nothing pending", async () =>
|
|
|
84
67
|
});
|
|
85
68
|
test("poll reports pending while a fresh registration is live", async () => {
|
|
86
69
|
const dir = newDir();
|
|
87
|
-
await runAccountRegisterStart({ accountId: "a", accountsDir: dir, clientId: "c"
|
|
70
|
+
await runAccountRegisterStart({ accountId: "a", accountsDir: dir, clientId: "c" });
|
|
88
71
|
const res = await runAccountRegisterPoll({ accountId: "a", accountsDir: dir });
|
|
89
72
|
assert.equal(res.status, "pending");
|
|
90
73
|
});
|
|
@@ -96,4 +79,72 @@ test("listConnected reports a stored account by email", () => {
|
|
|
96
79
|
assert.equal(connected[0].email, "u@e.com");
|
|
97
80
|
assert.equal(connected[0].status, "ok");
|
|
98
81
|
});
|
|
82
|
+
test("consent redirect is the loopback IP literal, never localhost or https", () => {
|
|
83
|
+
const url = new URL(buildConsentUrl({ clientId: "cid", state: "nonce", challenge: "ch", accountId: "a" }));
|
|
84
|
+
const r = url.searchParams.get("redirect_uri");
|
|
85
|
+
assert.equal(r, LOOPBACK_REDIRECT_URI);
|
|
86
|
+
assert.equal(r, "http://127.0.0.1:8571");
|
|
87
|
+
// RFC 8252 s7.3 port-agnostic matching covers the IP literal only.
|
|
88
|
+
assert.ok(!r.includes("localhost"), "must not use the localhost hostname");
|
|
89
|
+
// Desktop clients reject https redirects outright.
|
|
90
|
+
assert.ok(!r.startsWith("https"), "must not be https");
|
|
91
|
+
assert.ok(!r.includes("?"), "no query string: Google matches redirect_uri exactly");
|
|
92
|
+
});
|
|
93
|
+
test("normalisePastedCode accepts every shape a user actually pastes", () => {
|
|
94
|
+
// Bare value.
|
|
95
|
+
assert.equal(normalisePastedCode("4/0AbCd"), "4/0AbCd");
|
|
96
|
+
assert.equal(normalisePastedCode(" 4/0AbCd "), "4/0AbCd");
|
|
97
|
+
// Google's loopback redirect ALWAYS appends scope, so the address bar shows a
|
|
98
|
+
// full URL. This is the shape the tool text actually asks the user to copy.
|
|
99
|
+
assert.equal(normalisePastedCode("http://127.0.0.1:8571/?code=4/0AbCd&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar"), "4/0AbCd");
|
|
100
|
+
// The query fragment alone, copied without the origin.
|
|
101
|
+
assert.equal(normalisePastedCode("code=4/0AbCd&scope=https%3A%2F%2Fx"), "4/0AbCd");
|
|
102
|
+
assert.equal(normalisePastedCode("?code=4/0AbCd&scope=x"), "4/0AbCd");
|
|
103
|
+
// A bare code=… with no other parameters.
|
|
104
|
+
assert.equal(normalisePastedCode("code=4/0AbCd"), "4/0AbCd");
|
|
105
|
+
// Percent-escaped bare value copied from a browser that escaped the slash.
|
|
106
|
+
assert.equal(normalisePastedCode("4%2F0AbCd"), "4/0AbCd");
|
|
107
|
+
});
|
|
108
|
+
test("normalisePastedCode never throws on a malformed percent escape", () => {
|
|
109
|
+
// decodeURIComponent throws URIError on a stray %, and it is all-or-nothing:
|
|
110
|
+
// a string with one bad escape cannot be partially decoded. The contract is
|
|
111
|
+
// therefore "return it untouched", so the user gets Google's legible
|
|
112
|
+
// invalid_grant rather than "URI malformed" from our own parser.
|
|
113
|
+
assert.doesNotThrow(() => normalisePastedCode("4%2F0Ab%zz"));
|
|
114
|
+
assert.equal(normalisePastedCode("4%2F0Ab%zz"), "4%2F0Ab%zz");
|
|
115
|
+
assert.doesNotThrow(() => normalisePastedCode("%"));
|
|
116
|
+
});
|
|
117
|
+
test("submit exchanges with the identical loopback redirect and stores the email", async () => {
|
|
118
|
+
const dir = newDir();
|
|
119
|
+
await runAccountRegisterStart({ accountId: "a", accountsDir: dir, clientId: "cid" });
|
|
120
|
+
const calls = [];
|
|
121
|
+
const fake = (async (url, init = {}) => {
|
|
122
|
+
calls.push({ url: String(url), body: String(init.body ?? "") });
|
|
123
|
+
if (String(url).includes("/token")) {
|
|
124
|
+
return new Response(JSON.stringify({ access_token: "AT", refresh_token: "RT", expires_in: 3600, scope: CALENDAR_SCOPE }), { status: 200 });
|
|
125
|
+
}
|
|
126
|
+
return new Response(JSON.stringify({ id: "user@example.com" }), { status: 200 });
|
|
127
|
+
});
|
|
128
|
+
const res = await runAccountRegisterSubmit({
|
|
129
|
+
accountId: "a", accountsDir: dir, clientId: "cid", clientSecret: "sec", code: "4/0AbCd", fetchImpl: fake,
|
|
130
|
+
});
|
|
131
|
+
assert.equal(res.email, "user@example.com");
|
|
132
|
+
// The exchange redirect must be byte-identical to the authorize one.
|
|
133
|
+
assert.ok(calls[0].body.includes(encodeURIComponent(LOOPBACK_REDIRECT_URI)));
|
|
134
|
+
assert.ok(calls[0].body.includes("client_secret=sec"));
|
|
135
|
+
assert.equal(new PendingStore("a", dir).read(), null, "pending must be cleared on success");
|
|
136
|
+
});
|
|
137
|
+
test("submit refuses when no registration is pending", async () => {
|
|
138
|
+
await assert.rejects(() => runAccountRegisterSubmit({
|
|
139
|
+
accountId: "a", accountsDir: newDir(), clientId: "c", clientSecret: "s", code: "4/0x",
|
|
140
|
+
}), /no registration in progress/i);
|
|
141
|
+
});
|
|
142
|
+
test("submit preserves the Google error body on a failed exchange", async () => {
|
|
143
|
+
const dir = newDir();
|
|
144
|
+
await runAccountRegisterStart({ accountId: "a", accountsDir: dir, clientId: "cid" });
|
|
145
|
+
const fake = (async () => new Response('{"error":"invalid_grant"}', { status: 400 }));
|
|
146
|
+
await assert.rejects(() => runAccountRegisterSubmit({
|
|
147
|
+
accountId: "a", accountsDir: dir, clientId: "c", clientSecret: "s", code: "4/0x", fetchImpl: fake,
|
|
148
|
+
}), /invalid_grant/);
|
|
149
|
+
});
|
|
99
150
|
//# sourceMappingURL=account-register.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-register.test.js","sourceRoot":"","sources":["../../src/__tests__/account-register.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,OAAO,EACP,eAAe,EACf,uBAAuB,EACvB,sBAAsB,EACtB,aAAa,EACb,cAAc,EACd,
|
|
1
|
+
{"version":3,"file":"account-register.test.js","sourceRoot":"","sources":["../../src/__tests__/account-register.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/boot.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
const entry = join(dirname(fileURLToPath(import.meta.url)), "..", "index.js");
|
|
7
|
+
/** Boot the real server over stdio with an EMPTY environment.
|
|
8
|
+
*
|
|
9
|
+
* The registration test greps source text, which cannot catch a module-init
|
|
10
|
+
* throw. Plugin-system spawn enumerates tools with no per-account env, and a
|
|
11
|
+
* throw there empties the platform tool registry and breaks /agents
|
|
12
|
+
* resolution — a failure that only appears in production. This exercises the
|
|
13
|
+
* actual protocol instead. */
|
|
14
|
+
function probe() {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const child = spawn(process.execPath, [entry], {
|
|
17
|
+
env: { PATH: process.env.PATH ?? "" },
|
|
18
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
19
|
+
});
|
|
20
|
+
let out = "";
|
|
21
|
+
let err = "";
|
|
22
|
+
child.stdout.on("data", (d) => (out += String(d)));
|
|
23
|
+
child.stderr.on("data", (d) => (err += String(d)));
|
|
24
|
+
child.on("error", reject);
|
|
25
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "probe", version: "0" } } })}\n`);
|
|
26
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} })}\n`);
|
|
27
|
+
setTimeout(() => {
|
|
28
|
+
child.kill();
|
|
29
|
+
const names = [];
|
|
30
|
+
for (const line of out.split("\n")) {
|
|
31
|
+
if (!line.trim())
|
|
32
|
+
continue;
|
|
33
|
+
try {
|
|
34
|
+
const msg = JSON.parse(line);
|
|
35
|
+
if (msg.id === 2)
|
|
36
|
+
for (const t of msg.result?.tools ?? [])
|
|
37
|
+
names.push(t.name);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* partial frame */
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
resolve({ tools: names, stderr: err });
|
|
44
|
+
}, 2500);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
test("boots with empty env and enumerates twelve tools over the real protocol", async () => {
|
|
48
|
+
const { tools, stderr } = await probe();
|
|
49
|
+
assert.equal(tools.length, 12, `expected 12 tools, got ${tools.length}`);
|
|
50
|
+
assert.ok(tools.includes("google-account-register-submit"));
|
|
51
|
+
assert.match(stderr, /\[google-mcp\] ready/, "must reach ready, not throw at init");
|
|
52
|
+
assert.match(stderr, /clientIdConfigured=false clientSecretConfigured=false/);
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=boot.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot.test.js","sourceRoot":"","sources":["../../src/__tests__/boot.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { mkdtempSync } from "node:fs";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { TokenStore } from "../auth/token-store.js";
|
|
7
|
-
import { buildQuery, isExpiring } from "../lib/google-client.js";
|
|
7
|
+
import { buildQuery, isExpiring, accessTokenForTest } from "../lib/google-client.js";
|
|
8
8
|
function newDir() {
|
|
9
9
|
return mkdtempSync(join(tmpdir(), "gcal-c-"));
|
|
10
10
|
}
|
|
@@ -22,4 +22,29 @@ test("isExpiring is false outside the threshold", () => {
|
|
|
22
22
|
s.store("at", "rt", 3600, {});
|
|
23
23
|
assert.equal(isExpiring(s.read()), false);
|
|
24
24
|
});
|
|
25
|
+
test("refresh posts direct to Google with the client secret", async () => {
|
|
26
|
+
const dir = newDir();
|
|
27
|
+
const store = new TokenStore("a", dir, "u@e.com");
|
|
28
|
+
// Already expired, so accessToken must refresh.
|
|
29
|
+
store.store("old", "RT", -1, { email: "u@e.com" });
|
|
30
|
+
let seen = "";
|
|
31
|
+
const fake = (async (_u, init = {}) => {
|
|
32
|
+
seen = String(init.body ?? "");
|
|
33
|
+
return new Response(JSON.stringify({ access_token: "NEW", expires_in: 3600 }), { status: 200 });
|
|
34
|
+
});
|
|
35
|
+
const token = await accessTokenForTest({ accountId: "a", clientId: "cid", clientSecret: "sec", store }, fake);
|
|
36
|
+
assert.equal(token, "NEW");
|
|
37
|
+
assert.ok(seen.includes("grant_type=refresh_token"));
|
|
38
|
+
assert.ok(seen.includes("client_secret=sec"));
|
|
39
|
+
assert.ok(seen.includes("refresh_token=RT"));
|
|
40
|
+
});
|
|
41
|
+
test("a failed refresh throws and leaves held tokens intact", async () => {
|
|
42
|
+
const dir = newDir();
|
|
43
|
+
const store = new TokenStore("a", dir, "u@e.com");
|
|
44
|
+
store.store("old", "RT", -1, { email: "u@e.com" });
|
|
45
|
+
const fake = (async () => new Response('{"error":"invalid_grant"}', { status: 400 }));
|
|
46
|
+
await assert.rejects(() => accessTokenForTest({ accountId: "a", clientId: "c", clientSecret: "s", store }, fake), /invalid_grant/);
|
|
47
|
+
const after = new TokenStore("a", dir, "u@e.com").read();
|
|
48
|
+
assert.equal(after?.refreshToken, "RT", "a failed refresh must not clear credentials");
|
|
49
|
+
});
|
|
25
50
|
//# sourceMappingURL=google-client.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-client.test.js","sourceRoot":"","sources":["../../src/__tests__/google-client.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"google-client.test.js","sourceRoot":"","sources":["../../src/__tests__/google-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ const mcpRoot = join(here, "..", "..");
|
|
|
9
9
|
const EXPECTED = [
|
|
10
10
|
"google-account-register",
|
|
11
11
|
"google-account-register-poll",
|
|
12
|
+
"google-account-register-submit",
|
|
12
13
|
"google-calendar-list",
|
|
13
14
|
"google-calendar-event",
|
|
14
15
|
"google-calendar-create",
|
|
@@ -19,12 +20,12 @@ const EXPECTED = [
|
|
|
19
20
|
"google-account-info",
|
|
20
21
|
"google-account-list",
|
|
21
22
|
];
|
|
22
|
-
test("index.ts registers exactly the
|
|
23
|
+
test("index.ts registers exactly the twelve planned tools", () => {
|
|
23
24
|
const src = readFileSync(join(mcpRoot, "src/index.ts"), "utf-8");
|
|
24
25
|
const found = [...src.matchAll(/"(google-[a-z-]+)"/g)].map((m) => m[1]);
|
|
25
26
|
assert.deepEqual([...new Set(found)].sort(), [...EXPECTED].sort());
|
|
26
27
|
});
|
|
27
|
-
test("PLUGIN.md declares the same
|
|
28
|
+
test("PLUGIN.md declares the same twelve tools as index.ts", () => {
|
|
28
29
|
const fm = readFileSync(join(mcpRoot, "..", "PLUGIN.md"), "utf-8");
|
|
29
30
|
const declared = [...fm.matchAll(/^\s+-\s+name:\s*(google-[a-z-]+)\s*$/gm)].map((m) => m[1]);
|
|
30
31
|
assert.deepEqual(declared.sort(), [...EXPECTED].sort());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-registration.test.js","sourceRoot":"","sources":["../../src/__tests__/index-registration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,yBAAyB;AACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvC,MAAM,QAAQ,GAAG;IACf,yBAAyB;IACzB,8BAA8B;IAC9B,sBAAsB;IACtB,uBAAuB;IACvB,wBAAwB;IACxB,wBAAwB;IACxB,wBAAwB;IACxB,yBAAyB;IACzB,0BAA0B;IAC1B,qBAAqB;IACrB,qBAAqB;CACtB,CAAC;AAEF,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC3E,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IAC1E,MAAM,WAAW,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5E,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1E,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index-registration.test.js","sourceRoot":"","sources":["../../src/__tests__/index-registration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,yBAAyB;AACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEvC,MAAM,QAAQ,GAAG;IACf,yBAAyB;IACzB,8BAA8B;IAC9B,gCAAgC;IAChC,sBAAsB;IACtB,uBAAuB;IACvB,wBAAwB;IACxB,wBAAwB;IACxB,wBAAwB;IACxB,yBAAyB;IACzB,0BAA0B;IAC1B,qBAAqB;IACrB,qBAAqB;CACtB,CAAC;AAEF,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC3E,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IAC1E,MAAM,WAAW,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5E,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1E,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC"}
|