@rubytech/create-siteoffice-code 0.1.263 → 0.1.265
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 +1 -1
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +7 -2
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-search.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-search.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-search.test.js +106 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-search.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +37 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +12 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +175 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +239 -239
- package/payload/platform/plugins/admin/skills/superpowers-sprint/SKILL.md +3 -3
- package/payload/platform/plugins/admin/skills/upgrade/SKILL.md +2 -2
- package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +10 -10
- package/payload/platform/plugins/cloudflare/references/reset-guide.md +1 -1
- package/payload/platform/plugins/docs/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/docs/PLUGIN.md +6 -6
- package/payload/platform/plugins/docs/references/admin-session.md +1 -1
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
- package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +7 -7
- package/payload/platform/plugins/docs/references/linkedin-extension.md +4 -4
- package/payload/platform/plugins/docs/references/memory-guide.md +2 -2
- package/payload/platform/plugins/docs/references/neo4j.md +1 -1
- package/payload/platform/plugins/docs/references/outlook-guide.md +1 -1
- package/payload/platform/plugins/docs/references/platform.md +2 -2
- package/payload/platform/plugins/docs/references/plugins-guide.md +6 -6
- package/payload/platform/plugins/docs/references/troubleshooting.md +1 -1
- package/payload/platform/plugins/linkedin-extension/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/linkedin-extension/PLUGIN.md +1 -1
- package/payload/platform/plugins/linkedin-extension/extension/README.md +4 -4
- package/payload/platform/plugins/linkedin-extension/extension/assets/pill.css +1 -1
- package/payload/platform/plugins/linkedin-extension/extension/content/profile.js +2 -2
- package/payload/platform/plugins/linkedin-extension/extension/content/thread.js +2 -2
- package/payload/platform/plugins/linkedin-extension/extension/manifest.json +3 -3
- package/payload/platform/plugins/linkedin-extension/extension/options/options.html +3 -3
- package/payload/platform/plugins/linkedin-import/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/linkedin-import/PLUGIN.md +2 -2
- package/payload/platform/plugins/memory/references/graph-primitives.md +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +1 -1
- package/payload/platform/plugins/memory/references/schema-construction.md +1 -1
- package/payload/platform/plugins/memory/skills/conversation-archive-mcp/SKILL.md +2 -2
- package/payload/platform/plugins/notion-import/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/notion-import/PLUGIN.md +2 -2
- package/payload/platform/plugins/obsidian-import/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/obsidian-import/PLUGIN.md +2 -2
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +1 -1
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/attachments.md +1 -1
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md +1 -1
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/vault-structure.md +1 -1
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/wikilinks.md +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/outlook/references/auth.md +2 -2
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +1 -1
- package/payload/platform/plugins/sales/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/sales/PLUGIN.md +8 -8
- package/payload/platform/plugins/sales/references/comparisons.md +18 -18
- package/payload/platform/plugins/sales/references/competitive-positioning.md +5 -5
- package/payload/platform/plugins/sales/references/faq.md +22 -22
- package/payload/platform/plugins/sales/references/objection-handling.md +3 -3
- package/payload/platform/plugins/sales/references/pricing.md +16 -16
- package/payload/platform/plugins/slides/PROVENANCE.md +1 -1
- package/payload/platform/plugins/substack-import/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/substack-import/PLUGIN.md +2 -2
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-login-start.test.d.ts +2 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-login-start.test.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-login-start.test.js +16 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-login-start.test.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/format.d.ts +7 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/format.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/format.js +21 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/format.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +11 -19
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +1 -1
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +15 -13
- package/payload/platform/plugins/x-import/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/x-import/PLUGIN.md +2 -2
- package/payload/platform/scripts/check-skill-frontmatter.mjs +8 -5
- package/payload/platform/scripts/wifi-provision-server/server.js +1 -1
- package/payload/platform/scripts/wifi-provision.sh +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/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +15 -15
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +3 -3
- package/payload/platform/templates/specialists/.claude-plugin/plugin.json +1 -1
- package/payload/premium-plugins/venture-studio/skills/prototype-host/SKILL.md +1 -1
- package/payload/server/{adminuser-self-heal-YC47O34W.js → adminuser-self-heal-LC7HZAC6.js} +17 -9
- package/payload/server/{chunk-HYQNUVGO.js → chunk-PFF6I7KP.js} +1 -8
- package/payload/server/{chunk-ET3BKHKR.js → chunk-SOLVVUST.js} +11 -3
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/{AdminShell-8JLQM1Mz.js → AdminShell-BcHJy_Y2.js} +1 -1
- package/payload/server/public/assets/{Checkbox-CuFJh7lI.js → Checkbox-BY2lndUH.js} +1 -1
- package/payload/server/public/assets/{admin-D4nsdmKU.js → admin-C364q_-g.js} +1 -1
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-D-ODjJS5.js → architectureDiagram-Q4EWVU46-B9ik0JyO.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DFuVe8f9.js → blockDiagram-DXYQGD6D-C-w-AMRJ.js} +1 -1
- package/payload/server/public/assets/{browser-DIKmvDl5.js → browser-D66JrDpx.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CeVLfznT.js → c4Diagram-AHTNJAMY-BH-b0NZ1.js} +1 -1
- package/payload/server/public/assets/channel-5xoW3jfU.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-BTaLfR3a.js → chunk-336JU56O-CZHLbmJj.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-BzTbm4pP.js → chunk-426QAEUC-Qx6Ggp9s.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-SiUyOXVG.js → chunk-4TB4RGXK-J1Raz9l0.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-BcQ33LnT.js → chunk-5FUZZQ4R-D6TFnJVF.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-CT7C1Tik.js → chunk-5PVQY5BW-CC5aEdzK.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-CK3uI2u6.js → chunk-EDXVE4YY-Bfbcdcbt.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-Ddp8Otnw.js → chunk-ENJZ2VHE-7EKi84Dw.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DHTxymk_.js → chunk-ICPOFSXX-DIH1FT8n.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-Heg3hegz.js → chunk-OYMX7WX6-MgP02n7p.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-Cw2PR7aJ.js → chunk-U2HBQHQK-Cwmbq3M7.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-C7vBCAQQ.js → chunk-X2U36JSP-D65N3MkW.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-BS8Mybh1.js → chunk-YZCP3GAM-BkCiRhSG.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DmnbQp47.js → chunk-ZZ45TVLE-3BQY1FWN.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DRSNlIL5.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CYvRRfMp.js +1 -0
- package/payload/server/public/assets/clone-DvQUt_lq.js +1 -0
- package/payload/server/public/assets/{dagre-D4i6r8zi.js → dagre-DVhW13RD.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-znWS6Wh2.js → dagre-KV5264BT-CtfjEgDz.js} +1 -1
- package/payload/server/public/assets/{data-CHqF4bkS.js → data-uiOcPh8T.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-B6tQBN1Y.js → diagram-5BDNPKRD-C8AXEFch.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-t73WpYIY.js → diagram-G4DWMVQ6-B1P8Cwuz.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-Cm63UOJm.js → diagram-MMDJMWI5-QBVBnPmO.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-DuTSLjVB.js → diagram-TYMM5635-D2xE8fN8.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-sbHunitX.js → erDiagram-SMLLAGMA-DMee1Yih.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BWOrPuqz.js → flowDiagram-DWJPFMVM-DAd3JjoB.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-WaLMGard.js → ganttDiagram-T4ZO3ILL-Bs33CmKo.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CsbIgKDn.js → gitGraphDiagram-UUTBAWPF-NXc8aeps.js} +1 -1
- package/payload/server/public/assets/{graph-kwyPyf_b.js → graph-BSHyF267.js} +1 -1
- package/payload/server/public/assets/{graph-labels-pX3uIQys.js → graph-labels-D5ecmK-Z.js} +1 -1
- package/payload/server/public/assets/{graphlib-B8yKFZvc.js → graphlib-DN7sm5nK.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-Bd55TzsD.js → infoDiagram-42DDH7IO-D2g5STFr.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BFdOsA9f.js → ishikawaDiagram-UXIWVN3A-DBgfV-LB.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CMQ5XWO9.js → journeyDiagram-VCZTEJTY-Chh9lW9P.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-_RpNNRN7.js → kanban-definition-6JOO6SKY-buI2AXpi.js} +1 -1
- package/payload/server/public/assets/{line-SkFxjGJ8.js → line-C8OFYjrW.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-Ddok2IAe.js → mermaid-parser.core-BI5Ythbv.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-CihAjXBG.js → mermaid.core-C4dyl1Xw.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CJsq3hrO.js → mindmap-definition-QFDTVHPH-BbXKSpiv.js} +1 -1
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-CSgg8lBB.js → pieDiagram-DEJITSTG-C8-OIoUW.js} +1 -1
- package/payload/server/public/assets/{public-BA9P-3ZV.js → public-DGlZwbyw.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-TtHxMQtS.js → quadrantDiagram-34T5L4WZ-HOEtpu4n.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DdYupKtL.js → requirementDiagram-MS252O5E-DLxjohCO.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-BVfdyUnO.js → sankeyDiagram-XADWPNL6-E_O_HiFL.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-aFLOua6n.js → sequenceDiagram-FGHM5R23-C3-hhAup.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-_3Ix8SPZ.js → stateDiagram-FHFEXIEX-BhAmQ0XX.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Dhrf9hq_.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-Bs96WqZ7.js → timeline-definition-GMOUNBTQ-DpfZt7xI.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-L9uAHstA.css → useSelectionMode-98jrB9kD.css} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-DE_SxsN2.js → vennDiagram-DHZGUBPP-Ck2Ago5q.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-DTCdKs6v.js → wardleyDiagram-NUSXRM2D-Dgk8C9kF.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CNIrxADs.js → xychartDiagram-5P7HB3ND-BRBSl8ID.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +5 -5
- package/payload/server/public/public.html +4 -4
- package/payload/server/server.js +607 -5064
- package/payload/server/public/assets/channel-Db8J_1Rl.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DjquNGFr.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CcE8sWEP.js +0 -1
- package/payload/server/public/assets/clone-xa7JZYoY.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-eVQQEyxx.js +0 -1
- /package/payload/server/public/assets/{useSelectionMode-Da9Glxvu.js → useSelectionMode-80iYuGPa.js} +0 -0
|
@@ -38,7 +38,7 @@ Invoke `superpowers:using-git-worktrees`. The skill creates an isolated git work
|
|
|
38
38
|
|
|
39
39
|
After entering the worktree, install dependencies before building. Worktrees only contain tracked files, so `node_modules`, build artifacts, and untracked configuration are absent. Run `npm install` in every directory you plan to build from.
|
|
40
40
|
|
|
41
|
-
`getmaxy` is one repo with two parallel installer trees living as plain directories — `packages/create-maxy/` ships `@rubytech/create-maxy`, and `maxy-code/packages/create-maxy-code/` ships `@rubytech/create-maxy-code`. Neither is a submodule, subtree, or sibling repo. Build only the tree the sprint touches; if a build needs `platform/` deps, install in the matching tree (`platform/` at the root for legacy work, `maxy-code/platform/` for
|
|
41
|
+
`getmaxy` is one repo with two parallel installer trees living as plain directories — `packages/create-maxy/` ships `@rubytech/create-maxy`, and `maxy-code/packages/create-maxy-code/` ships `@rubytech/create-maxy-code`. Neither is a submodule, subtree, or sibling repo. Build only the tree the sprint touches; if a build needs `platform/` deps, install in the matching tree (`platform/` at the root for legacy work, `maxy-code/platform/` for SiteOffice Code work). If the sprint depends on recent main-branch work, merge main into the worktree branch at the start.
|
|
42
42
|
|
|
43
43
|
If already in a worktree (resuming a sprint), skip — but verify `node_modules` is present.
|
|
44
44
|
|
|
@@ -271,8 +271,8 @@ If any row is ⏳, render the commands the operator runs when ready. Publish-tre
|
|
|
271
271
|
|
|
272
272
|
| Installer tree | Installer directory | Brands directory | Default brand | Default package |
|
|
273
273
|
|---|---|---|---|---|
|
|
274
|
-
| Repo root (legacy
|
|
275
|
-
| `maxy-code/` (
|
|
274
|
+
| Repo root (legacy SiteOffice) | `packages/create-maxy/` | `brands/` | `maxy` | `@rubytech/create-maxy` |
|
|
275
|
+
| `maxy-code/` (SiteOffice Code) | `maxy-code/packages/create-maxy-code/` | `maxy-code/brands/` | `maxy-code` | `@rubytech/create-maxy-code` |
|
|
276
276
|
|
|
277
277
|
```bash
|
|
278
278
|
git push
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: upgrade
|
|
3
|
-
description: "Upgrade
|
|
3
|
+
description: "Upgrade SiteOffice to the latest published version by re-running the brand's installer when the operator asks."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Upgrade
|
|
7
7
|
|
|
8
|
-
The operator wants to upgrade
|
|
8
|
+
The operator wants to upgrade SiteOffice. Re-run the installer for the active brand; it is idempotent, brings the install to the latest published version, and restarts the brand service when it finishes.
|
|
9
9
|
|
|
10
10
|
## Resolve the brand package
|
|
11
11
|
|
|
@@ -52,7 +52,7 @@ The setup-done claim only fires when `curl -I https://<hostname>` issued from ou
|
|
|
52
52
|
|
|
53
53
|
## Identity model
|
|
54
54
|
|
|
55
|
-
- **Product identity** (
|
|
55
|
+
- **Product identity** (SiteOffice vs Real Agent) — known from `brand.json` (`productName`, `configDir`).
|
|
56
56
|
- **Cloudflare account identity** — `cert.pem` from OAuth. One account per brand per device.
|
|
57
57
|
- **Account binding drift** — `~/{configDir}/cloudflared/account-binding.json` is a historical drift marker. Reset by `rm -rf ~/.${BRAND}/cloudflared/` per `references/reset-guide.md` when switching accounts.
|
|
58
58
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# Manual Cloudflare tunnel setup — human runbook
|
|
2
2
|
|
|
3
|
-
Step-by-step recovery path for when the
|
|
3
|
+
Step-by-step recovery path for when the SiteOffice automation fails or is unavailable. Every command below is isolated in its own code block — nothing to parse from prose. Every path is brand-scoped so multiple brands can coexist on the same Linux user.
|
|
4
4
|
|
|
5
|
-
Prerequisites: `cloudflared` installed; the brand's VNC running on its own X display + rfbport + websockify + CDP port (per `brand.json`'s `vncDisplay`/`rfbPort`/`websockifyPort`/`cdpPort` fields — Tasks 553 + 924; defaults
|
|
5
|
+
Prerequisites: `cloudflared` installed; the brand's VNC running on its own X display + rfbport + websockify + CDP port (per `brand.json`'s `vncDisplay`/`rfbPort`/`websockifyPort`/`cdpPort` fields — Tasks 553 + 924; defaults SiteOffice=`:99`/`5900`/`6080`/`9222`, Real Agent=`:100`/`5901`/`6081`/`9223`, SiteOffice-2=`:101`/`5902`/`6082`/`9224`, SiteOffice-3=`:102`/`5903`/`6083`/`9225`, SiteOffice-4=`:103`/`5904`/`6084`/`9226`); SSH access to the device. The defaults table is historical — runtime readers (`paths.ts`, admin/MCP, `vnc.sh`, `test-laptop-vnc-boot.sh`) loud-fail with `reason=cdp-port-unresolved` if any of `rfbPort`/`websockifyPort`/`cdpPort` is missing from the live `brand.json`; the values above show what each brand's `brand.json` contains, not what the readers fall back to.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## Why two hostnames (admin and public)
|
|
10
10
|
|
|
11
|
-
Each
|
|
11
|
+
Each SiteOffice installation exposes two distinct surfaces from the same device and the same local port. The platform UI distinguishes them by the inbound `Host:` header.
|
|
12
12
|
|
|
13
13
|
- **admin hostname** — the operator's private chat + admin panel. PIN-protected. Only the owner uses it.
|
|
14
14
|
- **public hostname** — the public-facing agent (customer chat, marketing agent, WhatsApp webhook target). Reachable by anyone on the internet.
|
|
@@ -26,14 +26,14 @@ You have a Cloudflare account and a domain already on that account. You run ever
|
|
|
26
26
|
- `admin.<yourdomain>`
|
|
27
27
|
- `public.<yourdomain>`
|
|
28
28
|
|
|
29
|
-
### Mode B — you don't own a domain (
|
|
29
|
+
### Mode B — you don't own a domain (SiteOffice-provided subdomain)
|
|
30
30
|
|
|
31
|
-
First-time users without a Cloudflare account or domain use a subdomain of the
|
|
31
|
+
First-time users without a Cloudflare account or domain use a subdomain of the SiteOffice-operated domain (`maxy.bot`). SiteOffice owns the CF account and issues each user a token for a pre-created tunnel. Hostnames are:
|
|
32
32
|
|
|
33
33
|
- `<username>.maxy.bot` — admin surface (e.g. `neo.maxy.bot`, `joel.maxy.bot`)
|
|
34
|
-
- public surface is optional and, when enabled, uses a second
|
|
34
|
+
- public surface is optional and, when enabled, uses a second SiteOffice-issued subdomain.
|
|
35
35
|
|
|
36
|
-
Mode B skips Steps 1–5 of this runbook entirely — the user receives a token from
|
|
36
|
+
Mode B skips Steps 1–5 of this runbook entirely — the user receives a token from SiteOffice and only runs Step 6 (the connector) with `--token <X>`. Mode B is documented separately in `managed-tunnel-setup.md`.
|
|
37
37
|
|
|
38
38
|
**The rest of this runbook covers Mode A only.**
|
|
39
39
|
|
|
@@ -53,7 +53,7 @@ Apex hostnames (e.g. `maxy.chat`) cannot be routed by `cloudflared tunnel route
|
|
|
53
53
|
|
|
54
54
|
Run this once per shell session. Every subsequent step reads these variables. The `BRAND` value determines the on-disk directory (`${HOME}/.${BRAND}/cloudflared/`) so multiple brands on the same Linux user do not collide.
|
|
55
55
|
|
|
56
|
-
For
|
|
56
|
+
For SiteOffice:
|
|
57
57
|
|
|
58
58
|
```
|
|
59
59
|
export BRAND=maxy
|
|
@@ -365,7 +365,7 @@ cat "${CFG_DIR}/tunnel.state" | jq .tunnelId
|
|
|
365
365
|
|
|
366
366
|
Prints the UUID from Step 3. If it prints empty or null, the heredoc's env expansion failed — re-run the fail-fast guard.
|
|
367
367
|
|
|
368
|
-
**If you skip this step:** `systemctl --user status maxy.service` will show `ExecStartPre=resume-tunnel.sh (code=exited, status=0/SUCCESS)` (no error) but `ps -ef | grep '[c]loudflared'` will show no
|
|
368
|
+
**If you skip this step:** `systemctl --user status maxy.service` will show `ExecStartPre=resume-tunnel.sh (code=exited, status=0/SUCCESS)` (no error) but `ps -ef | grep '[c]loudflared'` will show no SiteOffice connector. Silent failure — the exact gap this step closes.
|
|
369
369
|
|
|
370
370
|
---
|
|
371
371
|
|
|
@@ -424,7 +424,7 @@ A non-530 response means the tunnel is live.
|
|
|
424
424
|
|
|
425
425
|
**Do NOT use `sudo cloudflared service install`.** It writes `/etc/systemd/system/cloudflared.service`, copies your config to `/etc/cloudflared/config.yml` (a system-wide path), and runs the connector as root — breaking brand isolation on any device that hosts more than one brand under the same Linux user. We tried this path on 2026-04-19; it created a duplicate connector parallel to the existing user-space one and required an explicit uninstall to undo.
|
|
426
426
|
|
|
427
|
-
The correct production pattern is already in place on every
|
|
427
|
+
The correct production pattern is already in place on every SiteOffice device: the brand's platform UI itself is a **user-space systemd service** at `~/.config/systemd/user/<brand>.service`, and that service spawns the cloudflared connector as an `ExecStartPre=` via `platform/scripts/resume-tunnel.sh`. When the user-space service restarts, the connector restarts with it. The connector runs as the Linux user (not root), its config is read from `${CFG_DIR}/config.yml` (brand-scoped, never `/etc`), and multiple brands coexist because each has its own unit file (`maxy.service`, `realagent.service`, etc.).
|
|
428
428
|
|
|
429
429
|
**Durability prerequisites (one-time per device):**
|
|
430
430
|
|
|
@@ -60,7 +60,7 @@ Re-run the setup flow from `references/manual-setup.md` § Step 1 with the opera
|
|
|
60
60
|
|
|
61
61
|
### Stop a token-mode connector
|
|
62
62
|
|
|
63
|
-
Token-mode tunnels are created by a central operator (e.g.
|
|
63
|
+
Token-mode tunnels are created by a central operator (e.g. SiteOffice-provided subdomains under `maxy.bot`). The connector runs with `--token <X>` and does not consult `cert.pem`, so the full-reset flow above (which authorises via cert.pem) cannot stop or delete it. Kill the process directly:
|
|
64
64
|
|
|
65
65
|
```
|
|
66
66
|
pkill -f 'cloudflared.*tunnel run --token'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docs",
|
|
3
|
-
"description": "Comprehensive user guide and platform documentation for
|
|
3
|
+
"description": "Comprehensive user guide and platform documentation for SiteOffice. Load references when users ask how-to, setup, or troubleshooting questions. Also contains platform architecture references for admin use.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docs
|
|
3
3
|
surface: platform
|
|
4
|
-
description: "Comprehensive user guide and platform documentation for
|
|
4
|
+
description: "Comprehensive user guide and platform documentation for SiteOffice. Load references when users ask how-to, setup, or troubleshooting questions. Also contains platform architecture references for admin use."
|
|
5
5
|
metadata: {"platform":{"always":true,"embed":["public","admin"],"pluginKey":"docs"}}
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# SiteOffice Documentation
|
|
9
9
|
|
|
10
|
-
This plugin is the single source of truth for
|
|
10
|
+
This plugin is the single source of truth for SiteOffice documentation. It ships with every install and is always the version the user has installed.
|
|
11
11
|
|
|
12
12
|
Load the relevant reference when the user asks a how-to question, requests help with setup, reports an issue, or wants to understand how something works.
|
|
13
13
|
|
|
14
14
|
## User Guide
|
|
15
15
|
|
|
16
|
-
Load these when users ask about
|
|
16
|
+
Load these when users ask about SiteOffice features or need guidance:
|
|
17
17
|
|
|
18
|
-
- **Getting started** → `references/getting-started.md` — first run, what
|
|
18
|
+
- **Getting started** → `references/getting-started.md` — first run, what SiteOffice is, how to use it
|
|
19
19
|
- **Plugins** → `references/plugins-guide.md` — what plugins are, how to install/remove them, the marketplace
|
|
20
|
-
- **Memory** → `references/memory-guide.md` — how
|
|
20
|
+
- **Memory** → `references/memory-guide.md` — how SiteOffice remembers things, what is stored, privacy
|
|
21
21
|
- **Graph view** → `references/graph.md` — the admin `/graph` page: node/edge display, zoom-adaptive Conversation labels, filter chips, trashed nodes
|
|
22
22
|
- **Contacts** → `references/contacts-guide.md` — adding, looking up, and managing contacts
|
|
23
23
|
- **Telegram** → `references/telegram-guide.md` — Telegram setup, the bot, daily use
|
|
@@ -28,7 +28,7 @@ The HMAC secret lives at `~/.${brand}/credentials/admin-session-secret` (mode `0
|
|
|
28
28
|
|
|
29
29
|
## SDK-resume contract on PIN-rebind
|
|
30
30
|
|
|
31
|
-
The Anthropic SDK is stateless against its own JSONL: every cold-create with `resume: <agentSessionId>` reads `${CLAUDE_CONFIG_DIR}/projects/<encoded-cwd>/<agentSessionId>.jsonl` verbatim. The
|
|
31
|
+
The Anthropic SDK is stateless against its own JSONL: every cold-create with `resume: <agentSessionId>` reads `${CLAUDE_CONFIG_DIR}/projects/<encoded-cwd>/<agentSessionId>.jsonl` verbatim. The SiteOffice graph (`Conversation.agentSessionId`) is just the pointer into the JSONL, not a parallel transcript. The PIN-rebind contract preserves this:
|
|
32
32
|
|
|
33
33
|
1. **Rehydrate** restores `(accountId, userId)` into the in-memory session. `sessionId` and `agentSessionId` are empty.
|
|
34
34
|
2. **Chat-route first POST** (`platform/ui/server/routes/admin/chat.ts`):
|
|
@@ -11,7 +11,7 @@ SDK-resume contract). This file is the index that points at them.
|
|
|
11
11
|
|
|
12
12
|
The `maxy-code/` tree does **not** ship a `.docs/platform.md` developer
|
|
13
13
|
doc. The legacy root tree (`getmaxy/`) carries one at
|
|
14
|
-
`.docs/platform.md` for the original
|
|
14
|
+
`.docs/platform.md` for the original SiteOffice installer; the SiteOffice Code tree
|
|
15
15
|
keeps its architecture surface in two places:
|
|
16
16
|
|
|
17
17
|
- `maxy-code-prd.md` at the repo root — product requirements and the
|
|
@@ -232,7 +232,7 @@ chat stays live.
|
|
|
232
232
|
SOUL / KNOWLEDGE) auto-save on type via
|
|
233
233
|
`POST /api/admin/sidebar-artefact-save`.
|
|
234
234
|
- Read-only artefacts (specialist agent templates) cannot be edited
|
|
235
|
-
because they ship with
|
|
235
|
+
because they ship with SiteOffice and would be overwritten on the next
|
|
236
236
|
install.
|
|
237
237
|
- PDF artefacts render inline; non-PDF binaries fall back to a Download
|
|
238
238
|
button when the browser has no native viewer.
|
|
@@ -6,7 +6,7 @@ Each installation has its own Cloudflare account. The **tunnel** sign-in is OAut
|
|
|
6
6
|
|
|
7
7
|
| Concept | Source |
|
|
8
8
|
|------|--------|
|
|
9
|
-
| **Product identity** (
|
|
9
|
+
| **Product identity** (SiteOffice vs Real Agent) | `brand.json` (`productName`, `configDir`) — known at install. |
|
|
10
10
|
| **Cloudflare account identity** | `cert.pem` from OAuth. One account per brand per device. |
|
|
11
11
|
| **Domain scope** (which zones the operator can route) | The operator picks the zone in the dashboard during OAuth or names it in chat; the agent can also enumerate zones via the API with a minted read-scoped token. |
|
|
12
12
|
| **Local tunnel state** | `~/{configDir}/cloudflared/` — `cert.pem`, `<UUID>.json`, `config.yml`, `alias-domains.json`. |
|
|
@@ -108,7 +108,7 @@ Diagnostic line per spawn (`~/.<brand>/logs/server.log`):
|
|
|
108
108
|
|
|
109
109
|
The `pty-spawn-start` line additionally carries `hooksResolved=<event1,event2,…>` — the hook event names Claude Code would actually load for that PTY (resolved by walking the same `$CLAUDE_CONFIG_DIR/settings.json` plus the cwd-to-.git path Claude Code itself uses). A Stop hook registered in a settings file outside the loader's scope shows up as a missing event in this list.
|
|
110
110
|
|
|
111
|
-
Zero `aboutOwnerBytes` on any admin spawn is a regression: the upstream resolver dropped the field entirely. The prose body always carries the unconditional MAXIMISE imperative on line two, so the byte count is positive even on a fresh-account spawn whose line one is `NOTHING`. Zero `dormantPluginsBytes` on a
|
|
111
|
+
Zero `aboutOwnerBytes` on any admin spawn is a regression: the upstream resolver dropped the field entirely. The prose body always carries the unconditional MAXIMISE imperative on line two, so the byte count is positive even on a fresh-account spawn whose line one is `NOTHING`. Zero `dormantPluginsBytes` on a SiteOffice install with `cloudflare` not enabled is likewise a regression. Zero `pluginManifestBytes` on any account with enabled plugins means the upstream walker failed silently.
|
|
112
112
|
|
|
113
113
|
The manager also runs a boot-time self-test that renders a fixture compose call against synthetic inputs and refuses to start if any sentinel is missing:
|
|
114
114
|
|
|
@@ -196,9 +196,9 @@ A separate operator-side harness at `platform/scripts/installer-device-verify.sh
|
|
|
196
196
|
|
|
197
197
|
## Plugin registration at install time
|
|
198
198
|
|
|
199
|
-
The installer registers Claude Code plugins on the device as the last step before the brand service starts. After registration, `claude plugin list` on the Pi shows every
|
|
199
|
+
The installer registers Claude Code plugins on the device as the last step before the brand service starts. After registration, `claude plugin list` on the Pi shows every SiteOffice platform plugin shipped by the brand, every premium sub-plugin shipped by the brand, and any external plugins the brand declares (e.g. Telegram, Discord, iMessage from `claude-plugins-official`). Spawned `claude` sessions inherit those plugins from `~/.claude/` — the session manager passes no `--mcp-config` argv.
|
|
200
200
|
|
|
201
|
-
**Where the manifests come from.** The
|
|
201
|
+
**Where the manifests come from.** The SiteOffice plugin source tree uses `PLUGIN.md` (YAML frontmatter) for plugin metadata, not Claude Code's native `.claude-plugin/plugin.json`. At bundle time, `scripts/generate-plugin-manifests.mjs` walks the payload and synthesises a Claude-Code-native `plugin.json` per plugin plus a `marketplace.json` at each tree root. The generator runs in `packages/create-maxy-code/scripts/bundle.js` after platform + premium plugins are copied into the payload, so the deployed install directory carries:
|
|
202
202
|
|
|
203
203
|
- `<INSTALL_DIR>/platform/plugins/<name>/.claude-plugin/plugin.json` per platform plugin
|
|
204
204
|
- `<INSTALL_DIR>/platform/plugins/.claude-plugin/marketplace.json` (marketplace `maxy-platform`)
|
|
@@ -247,17 +247,17 @@ External channel plugins (telegram, discord) are installed but not configured at
|
|
|
247
247
|
|
|
248
248
|
## Running multiple brands on one device
|
|
249
249
|
|
|
250
|
-
A single Pi or laptop can host more than one brand (for example
|
|
250
|
+
A single Pi or laptop can host more than one brand (for example SiteOffice and Real Agent) side by side. Each brand runs as its own service on its own port, with its own install directory and its own data. Installing one brand does not touch the other.
|
|
251
251
|
|
|
252
|
-
- **Separate:** each brand has its own install folder (`~/maxy/`, `~/realagent/`), its own config folder (`~/.maxy/`, `~/.realagent/`), its own web port, its own Cloudflare tunnel state, its own edge systemd unit (`maxy-edge.service` vs `realagent-edge.service`), and by default its own Neo4j database (
|
|
252
|
+
- **Separate:** each brand has its own install folder (`~/maxy/`, `~/realagent/`), its own config folder (`~/.maxy/`, `~/.realagent/`), its own web port, its own Cloudflare tunnel state, its own edge systemd unit (`maxy-edge.service` vs `realagent-edge.service`), and by default its own Neo4j database (SiteOffice on bolt port 7687, Real Agent on 7688). Action runner units are transient and per-invocation, not per-brand, so no naming conflict is possible.
|
|
253
253
|
- **Brand-isolated Neo4j:** when a brand provisions a dedicated Neo4j instance (any port other than 7687), the installer stops and disables the apt-package's system `neo4j.service` after enabling the brand-dedicated unit, so only one Neo4j process holds the shared `/var/lib/neo4j/run/` PID file. The seed step receives the brand-correct `NEO4J_URI` and `NEO4J_PASSWORD` as explicit environment variables — the seed script no longer carries a `bolt://localhost:7687` default. A failed dedicated start aborts the install loudly with a journalctl tail; there is no silent fallback to the system instance. Stop/disable targets the literal `neo4j.service` only, so peer brands running their own `neo4j-{brand}.service` are unaffected.
|
|
254
|
-
- **Peer-aware system-unit guard:** before stopping the system `neo4j.service`, the installer checks whether any other brand on the device still depends on it — that is, has `NEO4J_URI=bolt://localhost:7687` in its `~/.<peer>/.env`. If so, the system unit is left enabled and active, and the install log shows `[neo4j] system unit kept active — peer brand <name> depends on port 7687` instead of the usual `[neo4j] disabling system unit` line. This prevents a `create-realagent` install from disabling
|
|
254
|
+
- **Peer-aware system-unit guard:** before stopping the system `neo4j.service`, the installer checks whether any other brand on the device still depends on it — that is, has `NEO4J_URI=bolt://localhost:7687` in its `~/.<peer>/.env`. If so, the system unit is left enabled and active, and the install log shows `[neo4j] system unit kept active — peer brand <name> depends on port 7687` instead of the usual `[neo4j] disabling system unit` line. This prevents a `create-realagent` install from disabling SiteOffice's database on a host where SiteOffice still uses the shared system instance (the earlier platform fixes reproducer on Neo's laptop, 2026-04-28). On single-brand hosts and on multi-brand hosts where every peer runs a dedicated port, behaviour is unchanged. The dedicated unit exports `NEO4J_HOME=<per-brand-data-dir>` alongside `NEO4J_CONF`, so `server.directories.run`, `server.directories.plugins`, and `server.directories.import` resolve per-brand — no collision with `/var/lib/neo4j/run/neo4j.pid`. The conf sed-overrides, mkdir-p, chown, and unit-write are idempotent and re-run on every install, so a host whose prior install left a broken unit recovers on retry.
|
|
255
255
|
- **Shared:** both brands share the system Chromium/VNC stack, the Ollama model server, and the `cloudflared` command itself. Browser automation is serialised — one admin session at a time across both brands.
|
|
256
256
|
|
|
257
257
|
To install a second brand on a device that already runs the first, just run the other installer. No flags needed for isolation:
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
|
-
# Already running
|
|
260
|
+
# Already running SiteOffice on port 20000. Install Real Agent on a different port:
|
|
261
261
|
npx -y @rubytech/create-realagent --port 19500
|
|
262
262
|
```
|
|
263
263
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LinkedIn Extension — operator guide
|
|
2
2
|
|
|
3
|
-
Capture a LinkedIn profile or DM thread to your
|
|
3
|
+
Capture a LinkedIn profile or DM thread to your SiteOffice graph with one click. The plugin ships a small Chrome extension; the admin already knows how to receive its payloads.
|
|
4
4
|
|
|
5
5
|
## Install (one time)
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Capture a LinkedIn profile or DM thread to your Maxy graph with one click. The p
|
|
|
8
8
|
2. Toggle **Developer mode** on (top right).
|
|
9
9
|
3. Click **Load unpacked**.
|
|
10
10
|
4. Select `platform/plugins/linkedin-extension/extension/` on disk.
|
|
11
|
-
5. Click the puzzle icon → pin **
|
|
11
|
+
5. Click the puzzle icon → pin **SiteOffice LinkedIn Ingest** → open its options.
|
|
12
12
|
6. Paste two values:
|
|
13
13
|
- **Admin host** — your tunnel URL, e.g. `https://your-tunnel.example.com`.
|
|
14
14
|
- **Session key** — open your admin browser, copy the value of `session_key` from the cookie. (Same key the admin uses to authenticate every other admin request.)
|
|
@@ -16,7 +16,7 @@ Capture a LinkedIn profile or DM thread to your Maxy graph with one click. The p
|
|
|
16
16
|
|
|
17
17
|
## Use
|
|
18
18
|
|
|
19
|
-
- **Profile** — open any `https://www.linkedin.com/in/<slug>/` page. An **Add to
|
|
19
|
+
- **Profile** — open any `https://www.linkedin.com/in/<slug>/` page. An **Add to SiteOffice** pill appears in the top section. Click it. Within a few seconds the pill turns green: the profile is in your graph.
|
|
20
20
|
- **DM thread** — open any `https://www.linkedin.com/messaging/thread/<id>/` conversation. The same pill appears. Click it; the full transcript is captured plus the participants and any explicit commitments (meetings booked, actions promised, prices discussed).
|
|
21
21
|
- **Re-click** — the pill is idempotent. Re-clicking the same URL refreshes the document body and regenerates the summary; identities and entities `MERGE` rather than duplicate.
|
|
22
22
|
|
|
@@ -36,7 +36,7 @@ The plugin will never create `:Communication`, `:ConversationArchive` rows (i.e.
|
|
|
36
36
|
|
|
37
37
|
## When the pill turns amber
|
|
38
38
|
|
|
39
|
-
The pill shows **Sign in to
|
|
39
|
+
The pill shows **Sign in to SiteOffice** when your session key has expired. Click it to open the options page; paste a fresh `session_key` from your admin browser; save. The next click on the LinkedIn pill will succeed.
|
|
40
40
|
|
|
41
41
|
## When the pill turns red
|
|
42
42
|
|
|
@@ -114,7 +114,7 @@ Conversations and Messages carry role/channel sublabels so you can read the chat
|
|
|
114
114
|
|
|
115
115
|
**Bulk cleanup of conversations in chat:** when you ask {{productName}} to clean up conversations in bulk ("trash all empty conversations," "clean up the single-assistant tests"), the agent uses a deterministic selector with a fixed set of filter names — it cannot author custom delete queries. The server re-runs the same filter on every candidate before it trashes, so a stale list can't destroy something the filter wouldn't match now. If the filter matches nothing, {{productName}} reports "no candidates" and nothing happens.
|
|
116
116
|
|
|
117
|
-
The page reads only your own brand's Neo4j — a
|
|
117
|
+
The page reads only your own brand's Neo4j — a SiteOffice device and a Real Agent device share no graph state even when on the same laptop. No credentials are required; the view inherits your admin session.
|
|
118
118
|
|
|
119
119
|
**Typo-proof cypher.** When {{productName}} runs direct Cypher to answer a relational question, the query is checked against your Neo4j's live label and relationship-type taxonomy before it executes. Cypher that references an unknown name (an edge or label that does not exist in your graph) is rejected for writes and flagged with a warnings header for reads, so {{productName}} never silently acts on a query that targeted the wrong set of nodes. You should not see this — it runs invisibly — but it is the safety net that stops a fabricated edge name from producing "empty" results that are really just unreachable. Before acting on a bulk operation {{productName}} surfaces the result count and a sample; if it ever describes a cypher rejection, that means its first attempt was malformed and it corrected itself.
|
|
120
120
|
|
|
@@ -144,7 +144,7 @@ Every node also carries a provenance stamp — which agent wrote it, in which se
|
|
|
144
144
|
|
|
145
145
|
## Vertical schemas
|
|
146
146
|
|
|
147
|
-
On top of the base graph, each brand boots one optional **vertical** — an extra set of entity types tailored to a trade. The vertical is named by `brand.json#vertical` and defined in a `schema-<name>.md` reference; the memory plugin loads it at startup and validates every write against base + the active vertical. Real Agent boots `schema-estate-agent` (Listing, Property, Viewing, Offer). SiteOffice boots `schema-construction`, which adds the building-contractor entities — `Job`, `LineItem`, `Valuation`, `Milestone`, `QuoteDocument`, `VariationNote`, `InboundInvoice`, `SubContractor`, `TimeLog`, `SubInvoice`, `WhatsAppGroup` — grounded in real builder job folders so a job's quote, valuations, variations, supplier invoices, and subcontractor timesheets all hang off one `Job` node. The default
|
|
147
|
+
On top of the base graph, each brand boots one optional **vertical** — an extra set of entity types tailored to a trade. The vertical is named by `brand.json#vertical` and defined in a `schema-<name>.md` reference; the memory plugin loads it at startup and validates every write against base + the active vertical. Real Agent boots `schema-estate-agent` (Listing, Property, Viewing, Offer). SiteOffice boots `schema-construction`, which adds the building-contractor entities — `Job`, `LineItem`, `Valuation`, `Milestone`, `QuoteDocument`, `VariationNote`, `InboundInvoice`, `SubContractor`, `TimeLog`, `SubInvoice`, `WhatsAppGroup` — grounded in real builder job folders so a job's quote, valuations, variations, supplier invoices, and subcontractor timesheets all hang off one `Job` node. The default SiteOffice brand boots no vertical (base graph only).
|
|
148
148
|
|
|
149
149
|
## Public-facing summaries for customer-readable subjects
|
|
150
150
|
|
|
@@ -4,7 +4,7 @@ The `outlook` plugin gives the admin agent read-only access to Microsoft 365 / O
|
|
|
4
4
|
|
|
5
5
|
## Quickstart
|
|
6
6
|
|
|
7
|
-
1. **Register an Entra app once per
|
|
7
|
+
1. **Register an Entra app once per SiteOffice install** — see `platform/plugins/outlook/references/auth.md` for full steps. Set `OUTLOOK_CLIENT_ID` (and `OUTLOOK_TENANT_ID`, default `common`) in the operator's environment.
|
|
8
8
|
2. **Per account: register the Outlook account** — in admin chat, ask the agent to "register my Outlook account". The agent runs `outlook-account-register`, which prints an authorization URL.
|
|
9
9
|
3. **Open the URL in the VNC browser** — sign in to your Microsoft account, consent to the requested scopes (`offline_access`, `User.Read`, `Mail.Read`, `Calendars.Read`, `Contacts.Read`).
|
|
10
10
|
4. **Done.** Subsequent tool calls (mail, calendar, contacts) use the persisted refresh token transparently.
|
|
@@ -80,9 +80,9 @@ There is no dashboard, no settings panel, no menus. Everything is done through c
|
|
|
80
80
|
|
|
81
81
|
The chat input auto-grows as you type — it expands to fit your message and shrinks back when you delete text. You can also drag the resize handle above the input to set a custom height.
|
|
82
82
|
|
|
83
|
-
The admin interface is a three-pane layout: a sidebar on the left with navigation (Sessions, People, Agents, Projects, Tasks, Artefacts) and your recent conversations; the chat in the middle; and an artefact pane on the right that opens when you select a document, click a project, or open Browser, Data, or Graph from the menu, holding the surface side-by-side with the conversation so the chat stays live while you work in it. At the very top of the sidebar — above the nav rows — a borderless row holds two controls: a "+ New session" button on the left that spawns a fresh Claude Code session, and a Mode trigger on the right showing the current permission mode (Ask, Accept edits, Plan, or Auto). The sidebar's vertical order is: new-session strip first, then the nav (Sessions, People, Agents, Projects, Tasks, Artefacts), then the sessions list, then the footer. Both controls render as plain text-plus-icon affordances with no surrounding rectangle. The "+ New session" button is a text-width hit target — its clickable area is exactly the icon plus label, not the whole row — and shows no hover fill; the only hover feedback is the pointer cursor. The Mode trigger is pushed flush to the right edge of the row. Clicking the Mode trigger opens a popover downward from the row whose header reads "Mode" and lists the four permission modes with the current selection check-marked. The sidebar's nav rows swap the list view in place: Sessions shows recent conversations, Projects shows your active work projects, and Artefacts lists every KnowledgeDocument plus this account's agent templates (your admin agent's IDENTITY, SOUL, and KNOWLEDGE files plus one entry per enabled specialist). Each recent session row carries a three-state indicator: three pulsing dots when the session is busy (currently processing a turn), a solid sage dot when it is idle (live PTY waiting for input), and a hollow ring when it is archived (PTY exited, JSONL on disk for audit). The list itself splits into three views via a segmented control above the rows: **Active** shows every live session, **Archived** shows every JSONL on disk whose PTY has exited, and **All** shows both. The view choice persists across reloads. An "Include subagents" toggle inside the Active view surfaces specialist spawns (database-operator, premium-plugin agents, anything spawned with a `--agent` flag) which are hidden by default so the list reflects what you started directly. Each row also carries a small uppercase badge — `admin` for operator-driven sessions, the specialist name (for example `db-op`) for background work — so the source of any row is unambiguous at a glance. The People, Agents, and Tasks rows are graph shortcuts: clicking each opens the artefact-pane Graph filtered to every Person, every public Agent, or every Task in your account respectively, with no side-list, because the graph itself is the result. Public agents become first-class graph entities the moment you create them, with edges to their IDENTITY/SOUL/KNOWLEDGE files, edges to every knowledge document they have access to, and edges from every conversation they have handled, so a single Agents click reveals the whole shape of who knows what and who has been talking to whom. Click an artefact row to open the document. KnowledgeDocuments and your admin agent's templates are editable: type in the document and changes save automatically; specialist agent templates are read-only because they ship with
|
|
83
|
+
The admin interface is a three-pane layout: a sidebar on the left with navigation (Sessions, People, Agents, Projects, Tasks, Artefacts) and your recent conversations; the chat in the middle; and an artefact pane on the right that opens when you select a document, click a project, or open Browser, Data, or Graph from the menu, holding the surface side-by-side with the conversation so the chat stays live while you work in it. At the very top of the sidebar — above the nav rows — a borderless row holds two controls: a "+ New session" button on the left that spawns a fresh Claude Code session, and a Mode trigger on the right showing the current permission mode (Ask, Accept edits, Plan, or Auto). The sidebar's vertical order is: new-session strip first, then the nav (Sessions, People, Agents, Projects, Tasks, Artefacts), then the sessions list, then the footer. Both controls render as plain text-plus-icon affordances with no surrounding rectangle. The "+ New session" button is a text-width hit target — its clickable area is exactly the icon plus label, not the whole row — and shows no hover fill; the only hover feedback is the pointer cursor. The Mode trigger is pushed flush to the right edge of the row. Clicking the Mode trigger opens a popover downward from the row whose header reads "Mode" and lists the four permission modes with the current selection check-marked. The sidebar's nav rows swap the list view in place: Sessions shows recent conversations, Projects shows your active work projects, and Artefacts lists every KnowledgeDocument plus this account's agent templates (your admin agent's IDENTITY, SOUL, and KNOWLEDGE files plus one entry per enabled specialist). Each recent session row carries a three-state indicator: three pulsing dots when the session is busy (currently processing a turn), a solid sage dot when it is idle (live PTY waiting for input), and a hollow ring when it is archived (PTY exited, JSONL on disk for audit). The list itself splits into three views via a segmented control above the rows: **Active** shows every live session, **Archived** shows every JSONL on disk whose PTY has exited, and **All** shows both. The view choice persists across reloads. An "Include subagents" toggle inside the Active view surfaces specialist spawns (database-operator, premium-plugin agents, anything spawned with a `--agent` flag) which are hidden by default so the list reflects what you started directly. Each row also carries a small uppercase badge — `admin` for operator-driven sessions, the specialist name (for example `db-op`) for background work — so the source of any row is unambiguous at a glance. The People, Agents, and Tasks rows are graph shortcuts: clicking each opens the artefact-pane Graph filtered to every Person, every public Agent, or every Task in your account respectively, with no side-list, because the graph itself is the result. Public agents become first-class graph entities the moment you create them, with edges to their IDENTITY/SOUL/KNOWLEDGE files, edges to every knowledge document they have access to, and edges from every conversation they have handled, so a single Agents click reveals the whole shape of who knows what and who has been talking to whom. Click an artefact row to open the document. KnowledgeDocuments and your admin agent's templates are editable: type in the document and changes save automatically; specialist agent templates are read-only because they ship with SiteOffice and your edits would be overwritten on the next install. PDF artefacts render inline so you can read them without leaving the pane. If your browser doesn't have a built-in PDF viewer, a Download button appears instead. Artefacts that have no readable file backing them (orphan rows, files removed from disk, unsupported content types) show a one-line banner explaining the skip instead of opening to a blank pane. Click a project row to open the Graph view focused on that project's neighbourhood; clicking a second project swaps the focus rather than stacking on top. The sidebar's right edge is drag-resizable on every admin page (Sessions root, Graph, and Data): drag the handle to widen or narrow the sidebar, and your chosen width is remembered across reloads and shared across all three pages. The drag handle is mounted by each AdminShell consumer rather than by AdminShell itself, so any new admin route must include `<SidebarSplitter />` as a direct child of its `<AdminShell>` to pick up the shared width. The chat and artefact divider is also drag-resizable: drag the line between the columns to make either side wider; double-click it to reset to half of the available width (viewport minus sidebar), clamped to the chat and artefact min-width floors. Your chosen width is remembered across reloads. On wider screens (>1280px) all three panes are visible. The sidebar narrows at 1280px, the artefact pane hides at 1080px (Browser, Data, and Graph then open as full-window pages instead), and the sidebar collapses to a 56px icon rail at 820px. On every viewport the chat header reads left to right as a triptych: a dedicated sidebar toggle (the panel-right icon, which swaps to panel-right-open when the sidebar is showing), the brand mark next to the title in the centre, and the menu burger on the right. This header toggle is the sole sidebar-toggle button; the sidebar itself no longer carries a duplicate. Tap the sidebar toggle to show or hide the sidebar: on phones (<720px) it slides the drawer in or out, on wider screens it collapses or expands the sidebar column. The brand mark in the centre is decorative; clicks go through the dedicated toggle so the affordance is unambiguous. The drawer animation only fires on tap (220ms slide in or out); resizing your window across the 720px boundary snaps the layout without animation, so you never see a half-open flash. At ≤640px the session metadata pane stacks each label above its value instead of the desktop two-column grid, and the row of action buttons (Open in new tab / Download JSONL / View JSONL / Rename / Pin / Archive / End or Purge) collapses behind a single Actions trigger that opens a popover upward from the foot of the pane. Breakpoint summary: >1280px = full sidebar + chat + artefact pane (drag-resizable divider); 1280px→1080px = sidebar narrows; 1080px→820px = artefact pane hides (Browser/Data/Graph open as full-window pages instead); 820px→720px = sidebar collapses to 56px icon rail; ≤720px = sidebar becomes off-canvas drawer (vertical stack of nav, recents list, foot, the same shape as the desktop sidebar, just on top of the chat instead of beside it).
|
|
84
84
|
|
|
85
|
-
Page titles are brand-aware: the browser tab shows your product name (e.g. `Real Agent` instead of `
|
|
85
|
+
Page titles are brand-aware: the browser tab shows your product name (e.g. `Real Agent` instead of `SiteOffice`) on every shell — chat, graph, and data — so a non-default brand never leaks the default name in tab strips or browser history.
|
|
86
86
|
|
|
87
87
|
**Session lifecycle and reconcile model.** The sidebar Sessions list is driven by a single Server-Sent Events feed at `/api/admin/claude-sessions/events`. The session manager watches the two directories Claude Code writes (`${CLAUDE_CONFIG_DIR}/sessions/<pid>.json` for live state, `${CLAUDE_CONFIG_DIR}/projects/<slug>/<sid>.jsonl` for transcripts) and emits `row-created`, `row-updated`, `row-archived`, or `row-removed` deltas to every connected browser tab. Three real delete shapes map to deltas — there is no fourth: PID file gone with JSONL surviving demotes the row to `row-archived`; PID file gone with no JSONL ever written (a hidden spawn that exits before writing a JSONL) emits `row-removed` against the unindexed sessionId; a JSONL deletion against an already-unindexed row also emits `row-removed`. This branch reconciles transient hidden spawns — without it, ghost rows persist after a hidden spawn exits. On connect the manager replays the current row index so a freshly-opened tab catches up without polling, then streams deltas as files change on disk. Two open tabs see the same list within ~300ms of any spawn, status flip, or exit; no refresh button required for state to be current. The legacy `/list` fetch and `useAdminSessions` hook stay mounted to serve the ConversationsModal and the post-action reconcile path in `session-actions`, but the sidebar's visible rows come from the row store, not from `/list`. Each EventSource open emits `[admin-events] client-connected ip=<…> seeded-rows=<n>` server-side and `[admin-ui] session-row-store connected events-received=<n>` in the browser console; transport drops log `[admin-ui] session-row-store reconnect trigger=<auto|manual> attempt=<n> delay-ms=<n>` until the EventSource reattaches. The small dot at the right edge of the Active/Archived/All segmented control is the live-updates indicator: sage when the SSE feed is connected, grey when the feed has dropped. The grey state is an actionable button — clicking it cancels any pending backoff and re-opens the feed immediately, with the click logged as `trigger=manual` so manual retries are distinguishable from automatic ones in the console. The refresh icon at the top of the Sessions list is the operator-recoverable reconcile path against any SSE gap: it fetches `/api/admin/claude-sessions` and passes the authoritative id set to the row store, which evicts any indexed row that the server no longer reports. SSE replay only re-asserts currently-indexed rows and never emits `row-removed` for a row that vanished while disconnected, so without this manual surface a stale row can persist until the operator reloads the tab. Each click logs `[admin-ui] session-row-store reconcile evicted=<n> kept=<n>` when at least one row is evicted, and is silent otherwise.
|
|
88
88
|
|
|
@@ -78,9 +78,9 @@ Install verbatim:
|
|
|
78
78
|
|
|
79
79
|
Brand decides which premium plugins ship. The brand's `shipsPremiumBundles` field in `brand.json` is the gate; three shapes are supported:
|
|
80
80
|
|
|
81
|
-
- **omitted / false** — ship nothing from `premium-plugins/` (the legacy
|
|
81
|
+
- **omitted / false** — ship nothing from `premium-plugins/` (the legacy SiteOffice default).
|
|
82
82
|
- **`true`** — ship every bundle under `premium-plugins/*` (Real Agent / `realagent-code`).
|
|
83
|
-
- **`["bundle-a", "bundle-b"]`** — ship only the named bundle directories (
|
|
83
|
+
- **`["bundle-a", "bundle-b"]`** — ship only the named bundle directories (SiteOffice Code's `["venture-studio"]`). Names with no matching directory on disk are silently dropped; non-allowlisted bundles are stripped from any account that was previously stamped with them.
|
|
84
84
|
|
|
85
85
|
There is no per-account purchase record; the brand decides the shipping set.
|
|
86
86
|
|
|
@@ -95,7 +95,7 @@ There is no per-account purchase record; the brand decides the shipping set.
|
|
|
95
95
|
|
|
96
96
|
Some premium plugins are **bundles** — multiple sub-plugins shipped under one directory in `premium-plugins/`, each independently activatable. For example, Real Agent ships 10 sub-plugins covering different aspects of estate agency work. They are all enabled by default. Sub-plugins you don't want active can be turned off individually with "disable <name>"; enabling or disabling individual sub-plugins does not affect the others.
|
|
97
97
|
|
|
98
|
-
If you ask {{productName}} about a tool from a plugin your brand does not ship (for example, a
|
|
98
|
+
If you ask {{productName}} about a tool from a plugin your brand does not ship (for example, a SiteOffice install asking about a Real Agent Loop CRM tool), {{productName}} responds with a structured `<tool-surface-error>` envelope naming the missing plugin and the remedy, rather than improvising with a generic alternative.
|
|
99
99
|
|
|
100
100
|
**Public agent embedding:** Premium plugins marked as public-eligible have their full content (skills and reference knowledge) embedded in public agent prompts. This means a public agent for a Real Agent member can handle buyer enquiries, book viewings, deliver coaching content, and onboard new applicants — all powered by the premium plugin's domain knowledge. Plugins marked admin-only (listings, vendors, leads, business) are only available to the account owner's admin agent.
|
|
101
101
|
|
|
@@ -137,9 +137,9 @@ To edit an operator-authored skill later, ask {{productName}} to update it — t
|
|
|
137
137
|
|
|
138
138
|
## Brand Templating (for plugin and skill authors)
|
|
139
139
|
|
|
140
|
-
Skill content, plugin manifests, agent templates, and reference files reference the operator-visible brand name only via the literal `{{productName}}` placeholder. The platform substitutes from `brand.json.productName` at read time —
|
|
140
|
+
Skill content, plugin manifests, agent templates, and reference files reference the operator-visible brand name only via the literal `{{productName}}` placeholder. The platform substitutes from `brand.json.productName` at read time — SiteOffice installs render `SiteOffice`, Real Agent installs render `Real Agent`, all from the same source content.
|
|
141
141
|
|
|
142
|
-
**Author rule:** never write the literal string `
|
|
142
|
+
**Author rule:** never write the literal string `SiteOffice` (or any brand name) in shipped skill, plugin, or template content. Use `{{productName}}` whenever the operator should see the brand name. The audit grep `grep -rn "\bMaxy\b" platform/plugins/admin/skills/ platform/plugins/*/skills/ platform/templates/agents/` must return zero matches; a literal brand name is a defect, not a stylistic choice.
|
|
143
143
|
|
|
144
144
|
The runtime substitution happens at every read site that flows content into a system prompt or operator-visible UI: the admin agent's `plugin-read` tool (references + `PLUGIN.md`), the `skill-load` tool (SKILL.md by skill name — one-call resolver+reader, the canonical primitive for SKILL.md), the public agent's recursive plugin assembly, and `IDENTITY` / `SOUL` / `AGENTS` / `KNOWLEDGE` markdown reads. Missing or empty `productName` hard-fails — there is no fallback to a default brand string. See [.docs/agents.md](../../.docs/agents.md) § "Brand templating" for the full contract.
|
|
145
145
|
|
|
@@ -160,7 +160,7 @@ After this, every `console.error("[your-tool]...")` from any tool in the plugin
|
|
|
160
160
|
|
|
161
161
|
**How the tee decides which file to write to:** the platform sets `STREAM_LOG_PATH` as an environment variable on every MCP server spawn, pointing to the conversation-scoped stream log. The MCP server does not know about conversations — it just trusts `STREAM_LOG_PATH`. Multiple concurrent conversations produce multiple concurrent MCP server processes, each teeing to its own file; no cross-conversation leakage.
|
|
162
162
|
|
|
163
|
-
**Bash commands stream straight into the PTY.**
|
|
163
|
+
**Bash commands stream straight into the PTY.** SiteOffice Code's admin and public chat run on the native Claude Code PTY (Task 287). The per-conversation server-side stream log that the retired web-UI dispatcher tailed is gone; agent-invoked Bash commands (including direct `cloudflared` invocations for Cloudflare setup — Task 288) print their stdout and stderr directly, and the PTY renders the output in chat verbatim.
|
|
164
164
|
|
|
165
165
|
**Retrieve MCP diagnostic lines for a conversation:**
|
|
166
166
|
|
|
@@ -116,7 +116,7 @@ tail -200 ~/.maxy/logs/maxy-ui.log | rg '\[remote-auth\].*resolvedKind='
|
|
|
116
116
|
|
|
117
117
|
|
|
118
118
|
**A turn rendered in chat is missing on next page-refresh.** Pre-the 2026-05-07 mandate this was a class of silent failure — Neo4j persists were wrapped in a no-op error catch and a write that threw left the artefact "rendered then disappeared on resume". The 2026-05-07 mandate makes JSONL canonical: the resume route reads the SDK transcript file at `~/.claude/projects/<project-key>/<sessionId>.jsonl` first, supplements from Neo4j, and triggers async heal-on-resume writes for any turn the JSONL has but Neo4j does not. So a refreshed conversation always renders what the SDK saw, regardless of write outcome. If a heal write itself fails, the chat shows a top-of-conversation banner naming the count; if every heal succeeds the resume is silent and the missing rows are quietly restored to Neo4j. Greppable post-deploy invariants in the per-session stream log (`logs/claude-agent-stream-<sessionKey>.log`): `[admin-resume] reason=<…> source=<jsonl|jsonl-missing|neo4j-only>` (one per resume), `[admin-persist] convId=<8> writer=<…> outcome=<ok|fail|skip>` (per persist site), `[admin-persist-heal] convId=<8> turnIndex=<n> outcome=<ok|fail>` (per heal write). To force-audit a specific conversation against its Neo4j projection without re-executing it, run `tsx platform/scripts/admin-persist-audit.ts --conversation-id=<uuid> --account-id=<uuid> --session-id=<uuid>` — non-zero exit + per-divergence `[admin-persist-audit] expected=<message|component> missing reason=neo4j-row-absent` lines name what would have been silently lost pre-mandate.
|
|
119
|
-
**Wrong Claude account answering on a multi-brand device.** On a host running both
|
|
119
|
+
**Wrong Claude account answering on a multi-brand device.** On a host running both SiteOffice and Real Agent, each brand's admin agent reads its own `~/${brand.configDir}/.claude/.credentials.json`; there is no longer a shared `~/.claude/` thrashing them against one another. If a brand reports auth failures or appears to be operating against the wrong subscription, check three things:
|
|
120
120
|
1. `grep "\[claude-auth\] init" ~/.${brand}/logs/server.log | tail -1` — the resolved path must end with `~/.${brand}/.claude/.credentials.json`. If a `[claude-auth] WARN cross-brand-path-detected` line is present, the runtime is still pointing at `~/.claude/`; the brand main service did not pick up the `Environment=CLAUDE_CONFIG_DIR=` setting (re-run the brand installer to refresh the unit file).
|
|
121
121
|
2. `diff <(jq .claudeAiOauth.accessToken ~/.maxy/.claude/.credentials.json) <(jq .claudeAiOauth.accessToken ~/.realagent/.claude/.credentials.json)` — must be non-empty after each brand's operator has run `claude /login` against distinct Anthropic accounts; if it's empty, both brands are still logged in to the same account (operator action, not a code bug).
|
|
122
122
|
3. `grep "\[install\] claude-creds pickup" ~/.${brand}/logs/install-*.log` — fires once on the first post-Task-923 install of any brand and moves the legacy `~/.claude/.credentials.json` into that brand's path. Subsequent brands install with no credentials and require a fresh `claude /login` inside that brand's chat (which writes to the brand-scoped path because the systemd unit env is in scope).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linkedin-extension",
|
|
3
|
-
"description": "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension that injects an Add-to-
|
|
3
|
+
"description": "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension that injects an Add-to-SiteOffice pill into `linkedin.com/in/*` profiles and `linkedin.com/messaging/thread/*` DM threads. Each click scrapes the page DOM, POSTs to the admin `linkedin-ingest` route, which dispatches `database-operator` against the generic `document-ingest` skill. The plugin is the **first consumer** of the generic communication-ingest path; every future communication surface (email, Telegram, WhatsApp, group chats, voice memos) plugs into the same backend. Opt-in per brand.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: linkedin-extension
|
|
3
|
-
description: "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension that injects an Add-to-
|
|
3
|
+
description: "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension that injects an Add-to-SiteOffice pill into `linkedin.com/in/*` profiles and `linkedin.com/messaging/thread/*` DM threads. Each click scrapes the page DOM, POSTs to the admin `linkedin-ingest` route, which dispatches `database-operator` against the generic `document-ingest` skill. The plugin is the **first consumer** of the generic communication-ingest path; every future communication surface (email, Telegram, WhatsApp, group chats, voice memos) plugs into the same backend. Opt-in per brand."
|
|
4
4
|
tools: []
|
|
5
5
|
always: false
|
|
6
6
|
embed: false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SiteOffice LinkedIn Ingest — Chrome Extension
|
|
2
2
|
|
|
3
3
|
Unpacked-extension dev build. No Chrome Web Store publication.
|
|
4
4
|
|
|
@@ -7,16 +7,16 @@ Unpacked-extension dev build. No Chrome Web Store publication.
|
|
|
7
7
|
1. `chrome://extensions`
|
|
8
8
|
2. Enable **Developer mode** (top right).
|
|
9
9
|
3. **Load unpacked** → select this `extension/` directory.
|
|
10
|
-
4. Click the new puzzle icon → open **
|
|
10
|
+
4. Click the new puzzle icon → open **SiteOffice LinkedIn Ingest** options.
|
|
11
11
|
5. Paste your admin host (e.g. `https://your-tunnel.example.com`) and your admin **session key** (the value of `session_key` on the cookie or query in your admin browser). Save.
|
|
12
12
|
|
|
13
13
|
## Use
|
|
14
14
|
|
|
15
15
|
- Open any `https://www.linkedin.com/in/<slug>/` profile or `https://www.linkedin.com/messaging/thread/<id>/` thread.
|
|
16
|
-
- Click the **Add to
|
|
16
|
+
- Click the **Add to SiteOffice** pill at the top of the page.
|
|
17
17
|
- The pill turns green and writes one `:KnowledgeDocument` plus the entities the body assertively states.
|
|
18
18
|
|
|
19
|
-
If the pill turns amber and shows **Sign in to
|
|
19
|
+
If the pill turns amber and shows **Sign in to SiteOffice**, your session key has expired — open the options page and paste a fresh one.
|
|
20
20
|
|
|
21
21
|
## File layout
|
|
22
22
|
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
const detail = (reply && reply.detail) || ''
|
|
49
49
|
console.error(TAG_ERROR + ' reason=' + reason + ' detail=' + detail)
|
|
50
50
|
if (reason === 'auth') {
|
|
51
|
-
setState(pill, 'auth', 'Sign in to
|
|
51
|
+
setState(pill, 'auth', 'Sign in to SiteOffice')
|
|
52
52
|
pill.onclick = function () { chrome.runtime.openOptionsPage() }
|
|
53
53
|
return
|
|
54
54
|
}
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
pill.id = PILL_ID
|
|
68
68
|
pill.className = 'maxy-pill'
|
|
69
69
|
pill.type = 'button'
|
|
70
|
-
pill.textContent = 'Add to
|
|
70
|
+
pill.textContent = 'Add to SiteOffice'
|
|
71
71
|
pill.dataset.state = 'idle'
|
|
72
72
|
pill.addEventListener('click', function () { onClick(pill) })
|
|
73
73
|
anchor.prepend(pill)
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
const detail = (reply && reply.detail) || ''
|
|
51
51
|
console.error(TAG_ERROR + ' reason=' + reason + ' detail=' + detail)
|
|
52
52
|
if (reason === 'auth') {
|
|
53
|
-
setState(pill, 'auth', 'Sign in to
|
|
53
|
+
setState(pill, 'auth', 'Sign in to SiteOffice')
|
|
54
54
|
pill.onclick = function () { chrome.runtime.openOptionsPage() }
|
|
55
55
|
return
|
|
56
56
|
}
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
pill.id = PILL_ID
|
|
70
70
|
pill.className = 'maxy-pill'
|
|
71
71
|
pill.type = 'button'
|
|
72
|
-
pill.textContent = 'Add to
|
|
72
|
+
pill.textContent = 'Add to SiteOffice'
|
|
73
73
|
pill.dataset.state = 'idle'
|
|
74
74
|
pill.addEventListener('click', function () { onClick(pill) })
|
|
75
75
|
anchor.prepend(pill)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
|
-
"name": "
|
|
3
|
+
"name": "SiteOffice LinkedIn Ingest",
|
|
4
4
|
"version": "0.1.0",
|
|
5
|
-
"description": "Add an Add-to-
|
|
5
|
+
"description": "Add an Add-to-SiteOffice pill to LinkedIn profiles and DM threads. One click captures the page and posts to your SiteOffice admin.",
|
|
6
6
|
"permissions": ["storage", "activeTab"],
|
|
7
7
|
"host_permissions": ["https://www.linkedin.com/*"],
|
|
8
8
|
"background": {
|
|
@@ -27,6 +27,6 @@
|
|
|
27
27
|
"open_in_tab": true
|
|
28
28
|
},
|
|
29
29
|
"action": {
|
|
30
|
-
"default_title": "
|
|
30
|
+
"default_title": "SiteOffice LinkedIn Ingest — open options"
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
|
-
<title>
|
|
5
|
+
<title>SiteOffice LinkedIn Ingest — Options</title>
|
|
6
6
|
<style>
|
|
7
7
|
body { font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; max-width: 480px; margin: 32px auto; padding: 0 16px; color: #111; }
|
|
8
8
|
h1 { font-size: 18px; margin: 0 0 12px; }
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
</style>
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
|
-
<h1>
|
|
20
|
-
<p>Point the extension at your
|
|
19
|
+
<h1>SiteOffice LinkedIn Ingest</h1>
|
|
20
|
+
<p>Point the extension at your SiteOffice admin host and paste your admin session key. The pill on LinkedIn will then post one-click captures to your admin.</p>
|
|
21
21
|
|
|
22
22
|
<label for="admin-host">Admin host</label>
|
|
23
23
|
<input id="admin-host" type="url" placeholder="https://your-tunnel.example.com" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linkedin-import",
|
|
3
|
-
"description": "Import a LinkedIn Basic Data Export into the
|
|
3
|
+
"description": "Import a LinkedIn Basic Data Export into the SiteOffice Neo4j graph. Skill-only plugin owned by the librarian specialist. Opt-in per brand — not enabled by default.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|