@venturewild/workspace 0.5.3 → 0.6.0

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 (54) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +112 -112
  3. package/package.json +85 -85
  4. package/server/bin/wild-workspace.mjs +1096 -1096
  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 +974 -828
  10. package/server/src/bazaar/index.mjs +88 -88
  11. package/server/src/bazaar/mcp-server.mjs +429 -417
  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 +40 -40
  24. package/server/src/canvas/core.mjs +446 -446
  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 -108
  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 +3322 -3279
  34. package/server/src/listings-rails.mjs +156 -126
  35. package/server/src/logpaths.mjs +98 -98
  36. package/server/src/observability.mjs +45 -45
  37. package/server/src/operator.mjs +92 -92
  38. package/server/src/pairing.mjs +137 -137
  39. package/server/src/service.mjs +515 -515
  40. package/server/src/session-reporter.mjs +201 -201
  41. package/server/src/settings.mjs +145 -145
  42. package/server/src/share.mjs +182 -182
  43. package/server/src/skills.mjs +213 -213
  44. package/server/src/supervisor.mjs +647 -647
  45. package/server/src/support-consent.mjs +133 -133
  46. package/server/src/sync.mjs +248 -248
  47. package/server/src/transcript.mjs +121 -121
  48. package/server/src/turn-mcp.mjs +46 -46
  49. package/server/src/usage.mjs +405 -405
  50. package/server/src/workspace-presence.mjs +0 -0
  51. package/server/src/workspace-registry.mjs +295 -295
  52. package/server/src/workspaces.mjs +145 -145
  53. package/web/dist/assets/{index-BxTh3dyq.js → index-CSWkWdtM.js} +23 -23
  54. package/web/dist/index.html +1 -1
@@ -1,108 +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
- }
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
+ }