@venturewild/workspace 0.3.7 → 0.3.8

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.
Files changed (53) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +112 -112
  3. package/package.json +83 -83
  4. package/server/bin/wild-workspace.mjs +995 -995
  5. package/server/src/account.mjs +114 -114
  6. package/server/src/agent-login.mjs +146 -146
  7. package/server/src/agent-readiness.mjs +200 -200
  8. package/server/src/agent.mjs +468 -468
  9. package/server/src/bazaar/core.mjs +579 -579
  10. package/server/src/bazaar/index.mjs +75 -75
  11. package/server/src/bazaar/mcp-server.mjs +328 -328
  12. package/server/src/bazaar/mock-tickup.mjs +97 -97
  13. package/server/src/bazaar/preview-server.mjs +95 -95
  14. package/server/src/bazaar/seed-recipes/customer-feedback-form/know-how.md +23 -23
  15. package/server/src/bazaar/seed-recipes/customer-feedback-form/recipe.json +24 -24
  16. package/server/src/bazaar/seed-recipes/landing-page-launch/know-how.md +29 -29
  17. package/server/src/bazaar/seed-recipes/landing-page-launch/recipe.json +25 -25
  18. package/server/src/bazaar/seed-recipes/personal-portfolio/know-how.md +21 -21
  19. package/server/src/bazaar/seed-recipes/personal-portfolio/recipe.json +24 -24
  20. package/server/src/bazaar/seed-recipes/receipt-sorter/know-how.md +31 -31
  21. package/server/src/bazaar/seed-recipes/receipt-sorter/recipe.json +25 -25
  22. package/server/src/bazaar/seed-recipes/tickup-hr-matching/know-how.md +79 -79
  23. package/server/src/bazaar/seed-recipes/tickup-hr-matching/recipe.json +32 -32
  24. package/server/src/canvas/core.mjs +446 -421
  25. package/server/src/canvas/index.mjs +42 -42
  26. package/server/src/canvas/mcp-server.mjs +253 -253
  27. package/server/src/canvas-rails.mjs +108 -0
  28. package/server/src/config.mjs +404 -404
  29. package/server/src/daemon-bin.mjs +110 -110
  30. package/server/src/daemon-supervisor.mjs +285 -285
  31. package/server/src/doctor.mjs +375 -375
  32. package/server/src/inbox.mjs +86 -86
  33. package/server/src/index.mjs +2551 -2475
  34. package/server/src/logpaths.mjs +98 -98
  35. package/server/src/observability.mjs +45 -45
  36. package/server/src/operator.mjs +92 -92
  37. package/server/src/pairing.mjs +137 -137
  38. package/server/src/service.mjs +515 -515
  39. package/server/src/session-reporter.mjs +201 -201
  40. package/server/src/settings.mjs +145 -145
  41. package/server/src/share.mjs +182 -182
  42. package/server/src/skills.mjs +213 -213
  43. package/server/src/supervisor.mjs +647 -647
  44. package/server/src/support-consent.mjs +133 -133
  45. package/server/src/sync.mjs +248 -248
  46. package/server/src/transcript.mjs +121 -121
  47. package/server/src/turn-mcp.mjs +46 -46
  48. package/server/src/usage.mjs +405 -405
  49. package/web/dist/assets/index-B44y93r4.js +91 -0
  50. package/web/dist/assets/index-NXZN2LU2.css +1 -0
  51. package/web/dist/index.html +2 -2
  52. package/web/dist/assets/index-BxRx8EsD.js +0 -91
  53. package/web/dist/assets/index-DoOPBr3s.css +0 -1
@@ -0,0 +1,108 @@
1
+ // CanvasRails — per-identity canvas state on the rails (multi-host step 1).
2
+ //
3
+ // WHY: canvas layout/templates/user-theme used to be per-INSTALL (one folder on
4
+ // one machine). Multi-host (docs/multi-host-workspaces-design.md §8.1) makes them
5
+ // per-(workspace, person) on VW's rails, so a person's arrangement follows them
6
+ // across hosts/devices/reinstalls. The rails (bmo-sync) are the source of truth;
7
+ // the local ~/.wild-workspace/canvas/people/<personKey>/ files become a
8
+ // read-through + offline cache — the same "server is truth, cache below it"
9
+ // pattern lifted one level up.
10
+ //
11
+ // This is a thin client over bmo-sync's POST /api/canvas-state/{pull,push}
12
+ // (account-token self-authed, exactly like /api/telemetry). person_id and the
13
+ // workspace_slug are DERIVED server-side from the account token today — a caller
14
+ // can only touch its own account's row (no IDOR). Modeled on session-reporter.mjs.
15
+ //
16
+ // degrade-never-throw: every method fails soft (pull → {ok:false}, push → false)
17
+ // so the canvas keeps working from the local cache when the rails are down or the
18
+ // install has no account. `pull` distinguishes "rails answered, empty" (ok:true,
19
+ // state:null → migration may fire) from "rails unreachable" (ok:false → serve the
20
+ // local cache, do NOT migrate).
21
+
22
+ const DEFAULT_TIMEOUT_MS = 3000;
23
+
24
+ export class CanvasRails {
25
+ constructor({
26
+ bmoSyncUrl,
27
+ accountToken,
28
+ slug = null,
29
+ timeoutMs = DEFAULT_TIMEOUT_MS,
30
+ fetchImpl = (...a) => globalThis.fetch(...a),
31
+ } = {}) {
32
+ this.bmoSyncUrl = bmoSyncUrl ? bmoSyncUrl.replace(/\/$/, '') : null;
33
+ this.accountToken = accountToken || null;
34
+ this.slug = slug;
35
+ this.timeoutMs = timeoutMs;
36
+ this.fetchImpl = fetchImpl;
37
+ // Inert without a token (can't key it) or without a server URL. Unlike
38
+ // telemetry we do NOT exclude localhost — the e2e test points a local
39
+ // wild-workspace at a local bmo-sync, and dev installs have no account
40
+ // token anyway, so they stay inert without an extra guard.
41
+ this.capable = Boolean(this.accountToken) && Boolean(this.bmoSyncUrl);
42
+ }
43
+
44
+ async _post(path, body) {
45
+ if (!this.capable) return null;
46
+ const ctrl = new AbortController();
47
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
48
+ if (timer.unref) timer.unref();
49
+ try {
50
+ const r = await this.fetchImpl(`${this.bmoSyncUrl}${path}`, {
51
+ method: 'POST',
52
+ headers: { 'content-type': 'application/json' },
53
+ body: JSON.stringify({ account_token: this.accountToken, ...body }),
54
+ signal: ctrl.signal,
55
+ });
56
+ if (!r || !r.ok) return null;
57
+ return await r.json().catch(() => null);
58
+ } catch {
59
+ return null; // network / abort / parse — caller degrades to the local cache
60
+ } finally {
61
+ clearTimeout(timer);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Pull this person's canvas state from the rails.
67
+ * @returns {Promise<{ok: boolean, state: object|null}>}
68
+ * ok:true → the rails answered. state is the {layout,templates,userTheme}
69
+ * object, or null when the rails have nothing yet (→ migration may fire).
70
+ * ok:false → the rails were unreachable / not configured (→ serve the local cache).
71
+ */
72
+ async pull(workspaceSlug = this.slug) {
73
+ const resp = await this._post('/api/canvas-state/pull', { workspace_slug: workspaceSlug });
74
+ if (!resp || resp.ok !== true) return { ok: false, state: null };
75
+ // resp.json is the stored blob as a STRING (or null/absent when empty).
76
+ if (resp.json == null || resp.json === '') return { ok: true, state: null };
77
+ try {
78
+ const state = JSON.parse(resp.json);
79
+ return { ok: true, state: state && typeof state === 'object' ? state : null };
80
+ } catch {
81
+ return { ok: true, state: null }; // corrupt row → treat as empty
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Push this person's FULL canvas snapshot to the rails (last-write-wins; Node
87
+ * owns the merge, so the rails always receive the complete {layout,templates,
88
+ * userTheme}). Best-effort.
89
+ * @returns {Promise<boolean>} true iff the rails accepted the write.
90
+ */
91
+ async push(snapshot, workspaceSlug = this.slug) {
92
+ const resp = await this._post('/api/canvas-state/push', {
93
+ workspace_slug: workspaceSlug,
94
+ json: JSON.stringify(snapshot ?? {}),
95
+ });
96
+ return Boolean(resp && resp.ok === true);
97
+ }
98
+ }
99
+
100
+ /** Build the rails client from server config + account (or null when not logged in). */
101
+ export function createCanvasRails(config, account, fetchImpl) {
102
+ return new CanvasRails({
103
+ bmoSyncUrl: config?.bmoSyncServerUrl,
104
+ accountToken: account?.accountToken,
105
+ slug: account?.slug || config?.account?.slug || null,
106
+ fetchImpl,
107
+ });
108
+ }