aimeat 1.35.0 → 1.37.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.
- package/dist/.env.example +31 -0
- package/dist/locales/en.json +192 -27
- package/dist/locales/fi.json +192 -27
- package/dist/public/components/Markdown.js +9 -0
- package/dist/public/components/MemoryEmbed.js +151 -0
- package/dist/public/components/Mermaid.js +4 -1
- package/dist/public/components/NotificationBell.js +45 -19
- package/dist/public/css/components/markdown.css +15 -0
- package/dist/public/css/views/agents-detail.css +17 -0
- package/dist/public/css/views/portfolio.css +51 -0
- package/dist/public/css/views/profile.css +31 -0
- package/dist/public/js/services/agents.js +9 -0
- package/dist/public/js/services/organisms.js +37 -1
- package/dist/public/llms-template.txt +278 -0
- package/dist/public/spa.html +39 -2
- package/dist/public/sw.js +50 -34
- package/dist/public/views/my-company.js +13 -1
- package/dist/public/views/portfolio.js +267 -44
- package/dist/public/views/profile/agents/agent-card.js +4 -0
- package/dist/public/views/profile/agents/tab-contracts.js +112 -0
- package/dist/public/views/profile/landing-page.js +10 -1
- package/dist/public/views/profile/memory-tab.js +6 -2
- package/dist/public/views/profile/notifications-tab.js +9 -1
- package/dist/public/views/profile/organisms/home.js +23 -3
- package/dist/public/views/profile/organisms/participants-panel.js +89 -17
- package/dist/public/views/profile/organisms/workspace-apps.js +130 -0
- package/dist/public/views/profile/organisms/workspace.js +5 -0
- package/dist/public/views/profile/organisms-tab.js +1 -1
- package/dist/public/views/secretary.js +13 -0
- package/dist/scripts/archive-stale-versions.d.ts +33 -0
- package/dist/scripts/archive-stale-versions.d.ts.map +1 -0
- package/dist/scripts/archive-stale-versions.js +278 -0
- package/dist/scripts/archive-stale-versions.js.map +1 -0
- package/dist/src/cli/connect/mcp/tools/agent-management.d.ts +4 -1
- package/dist/src/cli/connect/mcp/tools/agent-management.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/agent-management.js +1 -1
- package/dist/src/cli/connect/mcp/tools/agent-management.js.map +1 -1
- package/dist/src/cli/init-wizard.d.ts.map +1 -1
- package/dist/src/cli/init-wizard.js +32 -0
- package/dist/src/cli/init-wizard.js.map +1 -1
- package/dist/src/config.d.ts +24 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +21 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/data/aimeat-iam-package.d.ts +10 -2
- package/dist/src/data/aimeat-iam-package.d.ts.map +1 -1
- package/dist/src/data/aimeat-iam-package.js +39 -10
- package/dist/src/data/aimeat-iam-package.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +802 -60
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/generated/prisma-postgres/edge.js +5 -3
- package/dist/src/generated/prisma-postgres/index-browser.js +2 -0
- package/dist/src/generated/prisma-postgres/index.d.ts +54 -2
- package/dist/src/generated/prisma-postgres/index.js +5 -3
- package/dist/src/generated/prisma-postgres/package.json +1 -1
- package/dist/src/generated/prisma-postgres/schema.prisma +2 -0
- package/dist/src/generated/prisma-postgres/wasm.js +5 -3
- package/dist/src/mcp/annotations.d.ts.map +1 -1
- package/dist/src/mcp/annotations.js +1 -0
- package/dist/src/mcp/annotations.js.map +1 -1
- package/dist/src/mcp/catalog/definitions.d.ts +3 -0
- package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions.js +21 -7
- package/dist/src/mcp/catalog/definitions.js.map +1 -1
- package/dist/src/mcp/catalog/surfaces.js +1 -1
- package/dist/src/mcp/catalog/surfaces.js.map +1 -1
- package/dist/src/mcp/core.js +1 -1
- package/dist/src/mcp/core.js.map +1 -1
- package/dist/src/mcp/extensions.d.ts.map +1 -1
- package/dist/src/mcp/extensions.js +21 -0
- package/dist/src/mcp/extensions.js.map +1 -1
- package/dist/src/mcp/memory-extended.js +1 -1
- package/dist/src/mcp/memory-extended.js.map +1 -1
- package/dist/src/mcp/organisms.d.ts +1 -0
- package/dist/src/mcp/organisms.d.ts.map +1 -1
- package/dist/src/mcp/organisms.js +23 -6
- package/dist/src/mcp/organisms.js.map +1 -1
- package/dist/src/mcp/workspaces.d.ts +7 -0
- package/dist/src/mcp/workspaces.d.ts.map +1 -1
- package/dist/src/mcp/workspaces.js +23 -5
- package/dist/src/mcp/workspaces.js.map +1 -1
- package/dist/src/middleware/subdomain.d.ts +9 -0
- package/dist/src/middleware/subdomain.d.ts.map +1 -1
- package/dist/src/middleware/subdomain.js +24 -3
- package/dist/src/middleware/subdomain.js.map +1 -1
- package/dist/src/models/schemas.d.ts +2 -0
- package/dist/src/models/schemas.d.ts.map +1 -1
- package/dist/src/models/schemas.js +2 -2
- package/dist/src/models/schemas.js.map +1 -1
- package/dist/src/routes/admin-features.js +1 -1
- package/dist/src/routes/admin-features.js.map +1 -1
- package/dist/src/routes/agent-onboarding.d.ts.map +1 -1
- package/dist/src/routes/agent-onboarding.js +44 -7
- package/dist/src/routes/agent-onboarding.js.map +1 -1
- package/dist/src/routes/agents.d.ts +8 -0
- package/dist/src/routes/agents.d.ts.map +1 -1
- package/dist/src/routes/agents.js +91 -5
- package/dist/src/routes/agents.js.map +1 -1
- package/dist/src/routes/app-grants.d.ts +5 -0
- package/dist/src/routes/app-grants.d.ts.map +1 -1
- package/dist/src/routes/app-grants.js +52 -20
- package/dist/src/routes/app-grants.js.map +1 -1
- package/dist/src/routes/boards.d.ts.map +1 -1
- package/dist/src/routes/boards.js +4 -1
- package/dist/src/routes/boards.js.map +1 -1
- package/dist/src/routes/bootstrap.d.ts +2 -0
- package/dist/src/routes/bootstrap.d.ts.map +1 -1
- package/dist/src/routes/bootstrap.js +5 -0
- package/dist/src/routes/bootstrap.js.map +1 -1
- package/dist/src/routes/capabilities.d.ts.map +1 -1
- package/dist/src/routes/capabilities.js +4 -1
- package/dist/src/routes/capabilities.js.map +1 -1
- package/dist/src/routes/catalogue.d.ts.map +1 -1
- package/dist/src/routes/catalogue.js +5 -4
- package/dist/src/routes/catalogue.js.map +1 -1
- package/dist/src/routes/extensions.d.ts.map +1 -1
- package/dist/src/routes/extensions.js +11 -0
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/ghii.d.ts.map +1 -1
- package/dist/src/routes/ghii.js +65 -17
- package/dist/src/routes/ghii.js.map +1 -1
- package/dist/src/routes/lib-editor.d.ts +26 -0
- package/dist/src/routes/lib-editor.d.ts.map +1 -0
- package/dist/src/routes/lib-editor.js +215 -0
- package/dist/src/routes/lib-editor.js.map +1 -0
- package/dist/src/routes/lib-markdown.d.ts +16 -1
- package/dist/src/routes/lib-markdown.d.ts.map +1 -1
- package/dist/src/routes/lib-markdown.js +281 -2
- package/dist/src/routes/lib-markdown.js.map +1 -1
- package/dist/src/routes/lib-organism.d.ts +28 -0
- package/dist/src/routes/lib-organism.d.ts.map +1 -0
- package/dist/src/routes/lib-organism.js +298 -0
- package/dist/src/routes/lib-organism.js.map +1 -0
- package/dist/src/routes/lib-portfolio-standalone.d.ts +22 -0
- package/dist/src/routes/lib-portfolio-standalone.d.ts.map +1 -0
- package/dist/src/routes/lib-portfolio-standalone.js +68 -0
- package/dist/src/routes/lib-portfolio-standalone.js.map +1 -0
- package/dist/src/routes/libs.d.ts +18 -0
- package/dist/src/routes/libs.d.ts.map +1 -1
- package/dist/src/routes/libs.js +116 -18
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/memory.d.ts +3 -0
- package/dist/src/routes/memory.d.ts.map +1 -1
- package/dist/src/routes/memory.js +58 -4
- package/dist/src/routes/memory.js.map +1 -1
- package/dist/src/routes/notifications.d.ts +5 -1
- package/dist/src/routes/notifications.d.ts.map +1 -1
- package/dist/src/routes/notifications.js +68 -3
- package/dist/src/routes/notifications.js.map +1 -1
- package/dist/src/routes/oauth-login.d.ts +26 -21
- package/dist/src/routes/oauth-login.d.ts.map +1 -1
- package/dist/src/routes/oauth-login.js +257 -229
- package/dist/src/routes/oauth-login.js.map +1 -1
- package/dist/src/routes/openrouter.d.ts +3 -0
- package/dist/src/routes/openrouter.d.ts.map +1 -1
- package/dist/src/routes/openrouter.js +10 -5
- package/dist/src/routes/openrouter.js.map +1 -1
- package/dist/src/routes/organisms.d.ts +6 -0
- package/dist/src/routes/organisms.d.ts.map +1 -1
- package/dist/src/routes/organisms.js +203 -16
- package/dist/src/routes/organisms.js.map +1 -1
- package/dist/src/routes/owners.d.ts.map +1 -1
- package/dist/src/routes/owners.js +12 -8
- package/dist/src/routes/owners.js.map +1 -1
- package/dist/src/routes/portfolio.d.ts +41 -1
- package/dist/src/routes/portfolio.d.ts.map +1 -1
- package/dist/src/routes/portfolio.js +94 -30
- package/dist/src/routes/portfolio.js.map +1 -1
- package/dist/src/routes/push.d.ts +15 -0
- package/dist/src/routes/push.d.ts.map +1 -1
- package/dist/src/routes/push.js +16 -1
- package/dist/src/routes/push.js.map +1 -1
- package/dist/src/routes/site.d.ts +2 -0
- package/dist/src/routes/site.d.ts.map +1 -1
- package/dist/src/routes/site.js +10 -2
- package/dist/src/routes/site.js.map +1 -1
- package/dist/src/routes/subdomains.d.ts +5 -0
- package/dist/src/routes/subdomains.d.ts.map +1 -1
- package/dist/src/routes/subdomains.js +56 -0
- package/dist/src/routes/subdomains.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +10 -15
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/server-bootstrap/static-files.d.ts.map +1 -1
- package/dist/src/server-bootstrap/static-files.js +9 -4
- package/dist/src/server-bootstrap/static-files.js.map +1 -1
- package/dist/src/services/access-guard.d.ts.map +1 -1
- package/dist/src/services/access-guard.js +7 -0
- package/dist/src/services/access-guard.js.map +1 -1
- package/dist/src/services/ai-completion.d.ts +2 -0
- package/dist/src/services/ai-completion.d.ts.map +1 -1
- package/dist/src/services/ai-completion.js +2 -1
- package/dist/src/services/ai-completion.js.map +1 -1
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +2 -0
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/ecosystem-access.d.ts +24 -8
- package/dist/src/services/ecosystem-access.d.ts.map +1 -1
- package/dist/src/services/ecosystem-access.js +27 -5
- package/dist/src/services/ecosystem-access.js.map +1 -1
- package/dist/src/services/generator-autopilot.d.ts +2 -0
- package/dist/src/services/generator-autopilot.d.ts.map +1 -1
- package/dist/src/services/generator-autopilot.js +3 -1
- package/dist/src/services/generator-autopilot.js.map +1 -1
- package/dist/src/services/iam/app-commands.d.ts +75 -0
- package/dist/src/services/iam/app-commands.d.ts.map +1 -0
- package/dist/src/services/iam/app-commands.js +55 -0
- package/dist/src/services/iam/app-commands.js.map +1 -0
- package/dist/src/services/iam/define-app-iam.d.ts +45 -0
- package/dist/src/services/iam/define-app-iam.d.ts.map +1 -0
- package/dist/src/services/iam/define-app-iam.js +75 -0
- package/dist/src/services/iam/define-app-iam.js.map +1 -0
- package/dist/src/services/iam/model.d.ts +113 -0
- package/dist/src/services/iam/model.d.ts.map +1 -0
- package/dist/src/services/iam/model.js +115 -0
- package/dist/src/services/iam/model.js.map +1 -0
- package/dist/src/services/mailbox-notification.d.ts +1 -0
- package/dist/src/services/mailbox-notification.d.ts.map +1 -1
- package/dist/src/services/mailbox-notification.js +3 -0
- package/dist/src/services/mailbox-notification.js.map +1 -1
- package/dist/src/services/notebook-ai.d.ts +2 -0
- package/dist/src/services/notebook-ai.d.ts.map +1 -1
- package/dist/src/services/notebook-ai.js +1 -1
- package/dist/src/services/notebook-ai.js.map +1 -1
- package/dist/src/services/notify.d.ts +11 -1
- package/dist/src/services/notify.d.ts.map +1 -1
- package/dist/src/services/notify.js +39 -2
- package/dist/src/services/notify.js.map +1 -1
- package/dist/src/services/oidc-providers.d.ts +94 -0
- package/dist/src/services/oidc-providers.d.ts.map +1 -0
- package/dist/src/services/oidc-providers.js +181 -0
- package/dist/src/services/oidc-providers.js.map +1 -0
- package/dist/src/services/organism-privacy.d.ts +46 -0
- package/dist/src/services/organism-privacy.d.ts.map +1 -0
- package/dist/src/services/organism-privacy.js +42 -0
- package/dist/src/services/organism-privacy.js.map +1 -0
- package/dist/src/services/organism-template.d.ts.map +1 -1
- package/dist/src/services/organism-template.js +3 -1
- package/dist/src/services/organism-template.js.map +1 -1
- package/dist/src/services/prompt-defaults.d.ts +2 -0
- package/dist/src/services/prompt-defaults.d.ts.map +1 -1
- package/dist/src/services/prompt-defaults.js +5 -0
- package/dist/src/services/prompt-defaults.js.map +1 -1
- package/dist/src/services/scheduler.d.ts.map +1 -1
- package/dist/src/services/scheduler.js +16 -4
- package/dist/src/services/scheduler.js.map +1 -1
- package/dist/src/services/skill-bundle/generator.d.ts +2 -0
- package/dist/src/services/skill-bundle/generator.d.ts.map +1 -1
- package/dist/src/services/skill-bundle/generator.js +3 -1
- package/dist/src/services/skill-bundle/generator.js.map +1 -1
- package/dist/src/services/workspace-access.d.ts +2 -0
- package/dist/src/services/workspace-access.d.ts.map +1 -1
- package/dist/src/services/workspace-access.js +20 -8
- package/dist/src/services/workspace-access.js.map +1 -1
- package/dist/src/services/workspace-engagements.d.ts +65 -0
- package/dist/src/services/workspace-engagements.d.ts.map +1 -0
- package/dist/src/services/workspace-engagements.js +92 -0
- package/dist/src/services/workspace-engagements.js.map +1 -0
- package/dist/src/services/workspace-meta.d.ts +9 -2
- package/dist/src/services/workspace-meta.d.ts.map +1 -1
- package/dist/src/services/workspace-meta.js +22 -2
- package/dist/src/services/workspace-meta.js.map +1 -1
- package/dist/src/storage/interface.d.ts +10 -2
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/interface.js.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts +1 -0
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +29 -2
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +1 -0
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +23 -8
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +9 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/identity.repository.d.ts +2 -0
- package/dist/src/storage/repositories/identity.repository.d.ts.map +1 -1
- package/dist/src/utils/env-config.d.ts.map +1 -1
- package/dist/src/utils/env-config.js +23 -0
- package/dist/src/utils/env-config.js.map +1 -1
- package/dist/src/utils/env-validator.d.ts.map +1 -1
- package/dist/src/utils/env-validator.js +8 -0
- package/dist/src/utils/env-validator.js.map +1 -1
- package/dist/src/utils/node-descriptor.d.ts.map +1 -1
- package/dist/src/utils/node-descriptor.js +1 -0
- package/dist/src/utils/node-descriptor.js.map +1 -1
- package/dist/static/app-catalog.html +8 -2
- package/dist/static/icons/badge-96.png +0 -0
- package/dist/static/icons/icon-192.png +0 -0
- package/dist/static/icons/icon-512.png +0 -0
- package/dist/static/manifest.json +19 -17
- package/dist/static/sw.js +13 -11
- package/package.json +3 -1
- package/prisma/schema.postgres.prisma +2 -0
- package/prisma/schema.prisma +2 -0
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* @description Profile tab for memory entries and file management — CRUD, search,
|
|
4
4
|
* visibility cycling, tag editing, sharing rules, and file upload with drag-and-drop.
|
|
5
5
|
* @version-history
|
|
6
|
+
* v2.3.0 — 2026-07-03 — 'members' visibility (readable by any logged-in node user)
|
|
7
|
+
* selectable in all visibility pickers (popover, detail select, bulk bar,
|
|
8
|
+
* create form, edit modal).
|
|
6
9
|
* v2.2.0 — 2026-06-13 — Render image memory values inline: a value that IS an image (a /v1/pub URL
|
|
7
10
|
* string or a { url, mime:image/* } object) shows a thumbnail above its raw JSON, via the shared
|
|
8
11
|
* ImageDeliverable renderer.
|
|
@@ -490,7 +493,7 @@ export default function MemoryTab({ session, showToast, onStats }) {
|
|
|
490
493
|
/* Visibility is edited via an explicit select inside the EXPANDED detail (and in the
|
|
491
494
|
edit modal) — the old per-row click-to-cycle pill meant one stray click in the list
|
|
492
495
|
could publish a memory. The list rows show a static badge only. */
|
|
493
|
-
const VIS_OPTIONS = ['private', 'owner', 'group', 'public'];
|
|
496
|
+
const VIS_OPTIONS = ['private', 'owner', 'group', 'members', 'public'];
|
|
494
497
|
|
|
495
498
|
// Quick visibility change from the expanded detail. 'group' needs a group pick,
|
|
496
499
|
// so it routes to the edit modal where the group select lives.
|
|
@@ -1374,6 +1377,7 @@ function MemoryForm({ onSave, onCancel, groups }) {
|
|
|
1374
1377
|
<option value="private">${t('profile.memory.visPrivate')}</option>
|
|
1375
1378
|
<option value="shared">${t('profile.memory.visShared')}</option>
|
|
1376
1379
|
<option value="group">${t('knowledge.visibility.group')}</option>
|
|
1380
|
+
<option value="members">${t('knowledge.visibility.members')}</option>
|
|
1377
1381
|
<option value="public">${t('profile.memory.visPublic')}</option>
|
|
1378
1382
|
</select>
|
|
1379
1383
|
</div>
|
|
@@ -1544,7 +1548,7 @@ function EditMemoryModal({ memKey, initialValue, initialVisibility, initialVersi
|
|
|
1544
1548
|
<label class="pf-label-inline">${t('profile.memory.visLabel')}</label>
|
|
1545
1549
|
<select class="input-field mem-vis-select" value=${vis}
|
|
1546
1550
|
onChange=${e => { setVis(e.target.value); if (e.target.value !== 'group') setGroupId(''); }}>
|
|
1547
|
-
${['private', 'owner', 'group', 'public'].map(v => html`<option key=${v} value=${v}>${t('knowledge.visibility.' + v)}</option>`)}
|
|
1551
|
+
${['private', 'owner', 'group', 'members', 'public'].map(v => html`<option key=${v} value=${v}>${t('knowledge.visibility.' + v)}</option>`)}
|
|
1548
1552
|
</select>
|
|
1549
1553
|
</div>
|
|
1550
1554
|
${vis === 'group' && html`
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* @description Profile tab for push notification and email notification preferences.
|
|
4
4
|
* @version-history
|
|
5
5
|
* v1.0.0 — 2026-03-17 — Refactor: replace inline styles with CSS classes; use ToggleSwitch and GlassCard from shared.js
|
|
6
|
+
* v1.1.0 — 2026-07-02 — Distinguish a browser-level permission denial from generic subscribe
|
|
7
|
+
* failures so the user knows to unblock notifications in browser settings.
|
|
6
8
|
*/
|
|
7
9
|
import { h } from 'preact';
|
|
8
10
|
import { useState, useEffect } from 'preact/hooks';
|
|
@@ -81,6 +83,11 @@ export default function NotificationsTab({ session, showToast }) {
|
|
|
81
83
|
setSubStatus(null);
|
|
82
84
|
return;
|
|
83
85
|
}
|
|
86
|
+
if (typeof Notification !== 'undefined' && Notification.permission === 'denied') {
|
|
87
|
+
showToast(t('profile.notifications.permissionDenied'), true);
|
|
88
|
+
setSubStatus(null);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
84
91
|
const reg = await navigator.serviceWorker.register('/sw.js');
|
|
85
92
|
await navigator.serviceWorker.ready;
|
|
86
93
|
|
|
@@ -114,7 +121,8 @@ export default function NotificationsTab({ session, showToast }) {
|
|
|
114
121
|
showToast(t('profile.notifications.subscribeSuccess'));
|
|
115
122
|
} catch (err) {
|
|
116
123
|
console.error('Push subscribe failed:', err);
|
|
117
|
-
|
|
124
|
+
const denied = typeof Notification !== 'undefined' && Notification.permission === 'denied';
|
|
125
|
+
showToast(t(denied ? 'profile.notifications.permissionDenied' : 'profile.notifications.subscribeFailed'), true);
|
|
118
126
|
}
|
|
119
127
|
setSubStatus(null);
|
|
120
128
|
}
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
* development timeline; rename the structure overview to "table of contents" (Osa A/C/D).
|
|
13
13
|
* v1.1.1 — 2026-06-23 — Mindmap space-node click now deep-links into the workspace on that space's
|
|
14
14
|
* tab (onOpenWs gained a second `space` arg); was opening the workspace overview.
|
|
15
|
+
* v1.2.0 — 2026-07-03 — Roster privacy: Settings → Access gains the "Member list" visibility
|
|
16
|
+
* select (member_visibility: signed-in default / members / admins / public) with an honest
|
|
17
|
+
* hint (hides the LIST, not content authorship); isMember now prefers `your_membership` from
|
|
18
|
+
* GET /:id since members[] can be roster-redacted for this caller.
|
|
15
19
|
*/
|
|
16
20
|
import { h } from 'preact';
|
|
17
21
|
import { useState, useEffect, useMemo, useRef } from 'preact/hooks';
|
|
@@ -50,7 +54,10 @@ export function OrganismHome({ org, ghii, showToast, initialSettings, onOpenWs,
|
|
|
50
54
|
|
|
51
55
|
const isCreator = org.creatorGhii === ghii;
|
|
52
56
|
const isAdmin = org.admins?.includes(ghii);
|
|
53
|
-
|
|
57
|
+
// members[] can be roster-redacted (memberVisibility) — your_membership from GET /:id is the
|
|
58
|
+
// caller-scoped truth, with the array as fallback for orgs whose roster this caller CAN see.
|
|
59
|
+
const [yourMembership, setYourMembership] = useState(null);
|
|
60
|
+
const isMember = (yourMembership?.status === 'active') || org.members?.includes(ghii);
|
|
54
61
|
const canEdit = isCreator || isAdmin;
|
|
55
62
|
const typeLabel = t(`organisms.types.${org.type}`) || org.type;
|
|
56
63
|
|
|
@@ -70,6 +77,7 @@ export function OrganismHome({ org, ghii, showToast, initialSettings, onOpenWs,
|
|
|
70
77
|
if (cancelled) return;
|
|
71
78
|
setGraph(g);
|
|
72
79
|
setReadme(full?.data?.readme || '');
|
|
80
|
+
setYourMembership(full?.data?.your_membership ?? null);
|
|
73
81
|
setTocSeed(toc || '');
|
|
74
82
|
};
|
|
75
83
|
loadExtras();
|
|
@@ -112,6 +120,7 @@ export function OrganismHome({ org, ghii, showToast, initialSettings, onOpenWs,
|
|
|
112
120
|
const baseline = useMemo(() => ({
|
|
113
121
|
name: org.name || '', description: org.description || '', type: org.type || 'community',
|
|
114
122
|
join_policy: org.joinPolicy || 'open', visibility: org.visibility || 'public',
|
|
123
|
+
member_visibility: org.memberVisibility || 'authenticated',
|
|
115
124
|
interests: [...(org.interests || [])],
|
|
116
125
|
}), [org]);
|
|
117
126
|
const [form, setForm] = useState(baseline);
|
|
@@ -124,12 +133,14 @@ export function OrganismHome({ org, ghii, showToast, initialSettings, onOpenWs,
|
|
|
124
133
|
const prev = prevBaselineRef.current;
|
|
125
134
|
const untouched = form.name === prev.name && form.description === prev.description
|
|
126
135
|
&& form.type === prev.type && form.join_policy === prev.join_policy
|
|
127
|
-
&& form.visibility === prev.visibility && form.
|
|
136
|
+
&& form.visibility === prev.visibility && form.member_visibility === prev.member_visibility
|
|
137
|
+
&& form.interests.join(' ') === prev.interests.join(' ');
|
|
128
138
|
if (untouched) setForm(baseline);
|
|
129
139
|
prevBaselineRef.current = baseline;
|
|
130
140
|
}, [baseline]); const dirty = form.name !== baseline.name || form.description !== baseline.description
|
|
131
141
|
|| form.type !== baseline.type || form.join_policy !== baseline.join_policy
|
|
132
142
|
|| form.visibility !== baseline.visibility
|
|
143
|
+
|| form.member_visibility !== baseline.member_visibility
|
|
133
144
|
|| form.interests.join(' ') !== baseline.interests.join(' ');
|
|
134
145
|
// Save doubles as the unsaved-changes indicator: enabled ⇔ something actually changed.
|
|
135
146
|
const saveEdit = async () => {
|
|
@@ -138,7 +149,8 @@ export function OrganismHome({ org, ghii, showToast, initialSettings, onOpenWs,
|
|
|
138
149
|
try {
|
|
139
150
|
const result = await orgService.updateOrganism(org.id, {
|
|
140
151
|
name: form.name.trim(), description: form.description.trim(),
|
|
141
|
-
type: form.type, join_policy: form.join_policy, visibility: form.visibility,
|
|
152
|
+
type: form.type, join_policy: form.join_policy, visibility: form.visibility,
|
|
153
|
+
member_visibility: form.member_visibility, interests: form.interests,
|
|
142
154
|
});
|
|
143
155
|
if (result?.ok !== false) { showToast(t('organisms.updated') || 'Organism updated'); onChanged?.(); }
|
|
144
156
|
else showToast(result?.error?.message || (t('organisms.updateError') || 'Failed to update'));
|
|
@@ -262,8 +274,16 @@ export function OrganismHome({ org, ghii, showToast, initialSettings, onOpenWs,
|
|
|
262
274
|
<option value="listed">${t('organisms.visListed') || 'Listed'}</option>
|
|
263
275
|
<option value="private">${t('organisms.visPrivate') || 'Private'}</option>
|
|
264
276
|
</select></label>
|
|
277
|
+
<label class="pj-field"><span>${t('organisms.memberVisLabel') || 'Member list'}</span>
|
|
278
|
+
<select class="input-field input-sm" value=${form.member_visibility} onChange=${(e) => setForm(f => ({ ...f, member_visibility: e.target.value }))}>
|
|
279
|
+
<option value="authenticated">${t('organisms.memberVis.authenticated') || 'Signed-in users'}</option>
|
|
280
|
+
<option value="members">${t('organisms.memberVis.members') || 'Members only'}</option>
|
|
281
|
+
<option value="admins">${t('organisms.memberVis.admins') || 'Admins only'}</option>
|
|
282
|
+
<option value="public">${t('organisms.memberVis.public') || 'Public (anyone)'}</option>
|
|
283
|
+
</select></label>
|
|
265
284
|
</div>
|
|
266
285
|
${hintText ? html`<div class="pj-form-hint">${hintText}</div>` : null}
|
|
286
|
+
<div class="pj-form-hint">${t('organisms.memberVisHint') || 'Who can see who belongs here. Hides the member LIST only — content authorship (comments, records, activity) stays visible, and the creator/admins are always shown.'}</div>
|
|
267
287
|
|
|
268
288
|
<div class="form-actions">
|
|
269
289
|
<button class="btn-primary btn-sm" onClick=${saveEdit} disabled=${saving || !dirty || !form.name.trim()}>
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* @usage import { ParticipantsPanel } from '/views/profile/organisms/participants-panel.js';
|
|
8
8
|
* @version-history
|
|
9
9
|
* v1.0.0 — 2026-06-19 — Extracted from organisms-tab.js during the module split.
|
|
10
|
+
* v1.1.0 — 2026-07-03 — Contract engagements: active/retired lifecycle chips (Adopt writes an active
|
|
11
|
+
* engagement, Retire flips it to retired), a legacy-agent one-click Retire, and re-adopt.
|
|
10
12
|
*/
|
|
11
13
|
import { h } from 'preact';
|
|
12
14
|
import { useState, useEffect } from 'preact/hooks';
|
|
@@ -14,6 +16,13 @@ import htm from 'htm';
|
|
|
14
16
|
import { onLiveUpdate } from '/lib/live-updates.js';
|
|
15
17
|
const html = htm.bind(h);
|
|
16
18
|
import { t } from '/js/i18n.js';
|
|
19
|
+
|
|
20
|
+
/** Short calendar day for a retired-since stamp (locale date, no time). '' if absent/unparseable. */
|
|
21
|
+
function fmtDay(iso) {
|
|
22
|
+
if (!iso) return '';
|
|
23
|
+
const d = new Date(iso);
|
|
24
|
+
return isNaN(d.getTime()) ? '' : d.toLocaleDateString();
|
|
25
|
+
}
|
|
17
26
|
import * as orgService from '/js/services/organisms.js';
|
|
18
27
|
import { listAgents, offersWorkspaceContract, contractNamesOf, adoptContractTask } from '/js/services/agents.js';
|
|
19
28
|
import { Mermaid } from '/components/Mermaid.js';
|
|
@@ -33,24 +42,64 @@ export function ParticipantsPanel({ orgId, wsId, showToast }) {
|
|
|
33
42
|
// straight away which of their agents can serve it.
|
|
34
43
|
const [contractAgents, setContractAgents] = useState([]);
|
|
35
44
|
const [adoptBusy, setAdoptBusy] = useState(''); // `${gaii}:${contract}` of the in-flight adopt
|
|
45
|
+
// Engagements = the first-class (agent × contract × workspace) bindings with an active/retired
|
|
46
|
+
// lifecycle. They drive the chips below (vs. the coarse "active here" record trace), so a contract
|
|
47
|
+
// can be adopted AND retired — a real off-switch, and retired ones stay as "used here until <date>".
|
|
48
|
+
const [engagements, setEngagements] = useState([]);
|
|
49
|
+
const [retireBusy, setRetireBusy] = useState('');
|
|
36
50
|
useEffect(() => {
|
|
37
51
|
listAgents().then(a => setContractAgents((a || []).filter(offersWorkspaceContract))).catch(() => {});
|
|
38
52
|
}, []);
|
|
39
|
-
//
|
|
40
|
-
|
|
53
|
+
// The engagement for (agent, contract) in THIS workspace, or undefined. contract '' = bare marker.
|
|
54
|
+
const engFor = (a, contract) => engagements.find(e => e.agent === a.gaii && (e.contract || '') === (contract || ''));
|
|
55
|
+
// One-click adoption: write an ACTIVE engagement (immediately visible + the source of truth the
|
|
56
|
+
// agent's loop obeys) AND queue the agreed adopt-contract task (docs/agent-workspace-contracts.md
|
|
57
|
+
// §7c) so the agent provisions its contract's spaces itself and reports back.
|
|
41
58
|
const adopt = async (a, contract) => {
|
|
42
59
|
const key = `${a.gaii}:${contract || ''}`;
|
|
43
60
|
setAdoptBusy(key);
|
|
44
61
|
try {
|
|
62
|
+
await orgService.activateEngagement(orgId, wsId, a.gaii, contract).catch(() => {});
|
|
45
63
|
const r = await adoptContractTask(a.name, { organismId: orgId, ws: wsId, contract });
|
|
46
64
|
if (r?.ok === false) showToast?.(r?.error?.message || (t('organisms.adoptFailed') || 'Could not queue the adoption task'));
|
|
47
65
|
else showToast?.((t('organisms.adoptQueued') || 'Adoption task queued for {agent} — it provisions the contract and reports back').replace('{agent}', a.display_name || a.name));
|
|
66
|
+
await refreshEngagements();
|
|
48
67
|
} catch (e) { showToast?.((e && e.message) || (t('organisms.adoptFailed') || 'Could not queue the adoption task')); }
|
|
49
68
|
finally { setAdoptBusy(''); }
|
|
50
69
|
};
|
|
70
|
+
// Retire a contract engagement — the agent's loop then skips this workspace. Works even with no
|
|
71
|
+
// prior engagement (a pre-contracts agent, "active here" only from record traces): it writes a
|
|
72
|
+
// retired marker so the history reads "used here until <date>".
|
|
73
|
+
const retire = async (a, contract) => {
|
|
74
|
+
const key = `${a.gaii}:${contract || ''}`;
|
|
75
|
+
setRetireBusy(key);
|
|
76
|
+
try {
|
|
77
|
+
const r = await orgService.retireEngagement(orgId, wsId, a.gaii, contract);
|
|
78
|
+
if (r?.ok === false) showToast?.(r?.error?.message || (t('organisms.retireFailed') || 'Could not retire the contract'));
|
|
79
|
+
else showToast?.((t('organisms.retired') || '{agent} retired from this workspace').replace('{agent}', a.display_name || a.name));
|
|
80
|
+
await refreshEngagements();
|
|
81
|
+
} catch (e) { showToast?.((e && e.message) || (t('organisms.retireFailed') || 'Could not retire the contract')); }
|
|
82
|
+
finally { setRetireBusy(''); }
|
|
83
|
+
};
|
|
84
|
+
// Retire every advertised contract of an agent from this workspace (used for a "legacy" agent that
|
|
85
|
+
// is working here — trace only — with no engagement records yet, so one click stops it entirely).
|
|
86
|
+
const retireAll = async (a) => {
|
|
87
|
+
const names = contractNamesOf(a);
|
|
88
|
+
const actions = names.length ? names : [''];
|
|
89
|
+
setRetireBusy(`${a.gaii}:*`);
|
|
90
|
+
try {
|
|
91
|
+
for (const c of actions) await orgService.retireEngagement(orgId, wsId, a.gaii, c).catch(() => {});
|
|
92
|
+
showToast?.((t('organisms.retired') || '{agent} retired from this workspace').replace('{agent}', a.display_name || a.name));
|
|
93
|
+
await refreshEngagements();
|
|
94
|
+
} finally { setRetireBusy(''); }
|
|
95
|
+
};
|
|
96
|
+
const refreshEngagements = () => orgService.getWorkspaceEngagements(orgId, wsId).then(setEngagements).catch(() => {});
|
|
51
97
|
useEffect(() => {
|
|
52
98
|
let cancelled = false;
|
|
53
|
-
const fetchIt = () =>
|
|
99
|
+
const fetchIt = () => {
|
|
100
|
+
orgService.getWorkspaceParticipants(orgId, wsId).then(d => { if (!cancelled) setData(d); }).catch(() => {});
|
|
101
|
+
orgService.getWorkspaceEngagements(orgId, wsId).then(e => { if (!cancelled) setEngagements(e); }).catch(() => {});
|
|
102
|
+
};
|
|
54
103
|
fetchIt();
|
|
55
104
|
const off = onLiveUpdate(['organisms'], fetchIt);
|
|
56
105
|
return () => { cancelled = true; off(); };
|
|
@@ -83,24 +132,47 @@ export function ParticipantsPanel({ orgId, wsId, showToast }) {
|
|
|
83
132
|
<div class="pj-part-agents">
|
|
84
133
|
${contractAgents.map(a => {
|
|
85
134
|
const names = contractNamesOf(a);
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// adoption for an agent that is already here is noise, and the task would be a
|
|
89
|
-
// no-op re-provision anyway.
|
|
90
|
-
const here = owners.some(o => o.isSelf && (o.agents || []).some(ag => ag.isOwn && ag.name === a.name));
|
|
91
|
-
// One adopt action per advertised contract (a single unnamed one falls back to
|
|
92
|
-
// the bare marker). The agent does the rest — join, provision, complete the task.
|
|
135
|
+
// One control per advertised contract (a single unnamed one falls back to the bare
|
|
136
|
+
// marker). The agent does the rest — join, provision, complete the task.
|
|
93
137
|
const actions = names.length ? names : [''];
|
|
138
|
+
// "Legacy" = the agent appears in the record traces (it has worked here) but carries
|
|
139
|
+
// no engagement record yet — it started before contracts were first-class. Offer one
|
|
140
|
+
// agent-level Retire so it can be stopped; new adopts get precise per-contract chips.
|
|
141
|
+
const traceHere = owners.some(o => o.isSelf && (o.agents || []).some(ag => ag.isOwn && ag.name === a.name));
|
|
142
|
+
const hasAnyEng = actions.some(c => engFor(a, c));
|
|
143
|
+
const legacyActive = traceHere && !hasAnyEng;
|
|
94
144
|
return html`
|
|
95
145
|
<span class="pj-part-agent own" key=${a.gaii} title=${a.gaii}>
|
|
96
146
|
${'📜 '}${a.display_name || a.name}
|
|
97
|
-
${
|
|
98
|
-
|
|
99
|
-
<button class="btn-
|
|
100
|
-
title=${t('organisms.
|
|
101
|
-
onClick=${() =>
|
|
102
|
-
|
|
103
|
-
|
|
147
|
+
${legacyActive ? html`
|
|
148
|
+
<span class="badge badge-success pj-mini" title=${t('organisms.contractActiveHint') || 'This agent already works in this workspace'}>${'✓ '}${t('organisms.contractActive') || 'active here'}</span>
|
|
149
|
+
<button class="btn-ghost btn-sm pj-retire-btn" disabled=${retireBusy === `${a.gaii}:*`}
|
|
150
|
+
title=${t('organisms.retireHint') || 'Stop this agent from working in THIS workspace — its loop skips it and the chip becomes “retired”. Its past work stays as history.'}
|
|
151
|
+
onClick=${() => retireAll(a)}>${retireBusy === `${a.gaii}:*` ? '…' : (t('organisms.retire') || 'Retire')}</button>`
|
|
152
|
+
: actions.map(c => {
|
|
153
|
+
const eng = engFor(a, c);
|
|
154
|
+
const label = c || (t('organisms.bareContract') || 'contract');
|
|
155
|
+
const bkey = `${a.gaii}:${c}`;
|
|
156
|
+
if (eng?.state === 'active') return html`
|
|
157
|
+
<span class="pj-eng" key=${c}>
|
|
158
|
+
<span class="badge badge-success pj-mini">${'✓ '}${label}</span>
|
|
159
|
+
<button class="btn-ghost btn-sm pj-retire-btn" disabled=${retireBusy === bkey}
|
|
160
|
+
title=${t('organisms.retireHint') || 'Stop this agent from working in THIS workspace — its loop skips it and the chip becomes “retired”. Its past work stays as history.'}
|
|
161
|
+
onClick=${() => retire(a, c)}>${retireBusy === bkey ? '…' : (t('organisms.retire') || 'Retire')}</button>
|
|
162
|
+
</span>`;
|
|
163
|
+
if (eng?.state === 'retired') return html`
|
|
164
|
+
<span class="pj-eng" key=${c}>
|
|
165
|
+
<span class="badge badge-muted pj-mini" title=${(t('organisms.retiredUntilHint') || 'Retired — this agent served here until this date. Its past work stays visible.')}>${label}${' · '}${t('organisms.retiredTag') || 'retired'} ${fmtDay(eng.retiredAt)}</span>
|
|
166
|
+
<button class="btn-outline btn-sm pj-adopt-btn" disabled=${adoptBusy === bkey}
|
|
167
|
+
onClick=${() => adopt(a, c)}>${adoptBusy === bkey ? '…' : (t('organisms.reAdopt') || 'Re-adopt')}</button>
|
|
168
|
+
</span>`;
|
|
169
|
+
return html`
|
|
170
|
+
<button class="btn-outline btn-sm pj-adopt-btn" key=${c} disabled=${adoptBusy === bkey}
|
|
171
|
+
title=${t('organisms.adoptHint') || 'Queue a task for this agent to adopt its contract into THIS workspace (it provisions the spaces itself)'}
|
|
172
|
+
onClick=${() => adopt(a, c)}>
|
|
173
|
+
${adoptBusy === bkey ? '…' : `${t('organisms.adoptContract') || 'Adopt'}${c ? ` ${c}` : ''}`}
|
|
174
|
+
</button>`;
|
|
175
|
+
})}
|
|
104
176
|
</span>`;
|
|
105
177
|
})}
|
|
106
178
|
</div>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file workspace-apps.js
|
|
3
|
+
* @description Workspace "Apps" strip — the apps pinned to this workspace (the `…meta.apps` binding
|
|
4
|
+
* record), rendered as launch cards like the landing wall, plus a creator/admin picker to pin/unpin
|
|
5
|
+
* published apps. Launching passes the workspace context in the URL FRAGMENT
|
|
6
|
+
* (#aimeat-ws={orgId}/{wsId}) — the fragment survives the app-origin redirect chain, so the app
|
|
7
|
+
* boots pinned to this workspace no matter which origin finally serves it. Pinning is
|
|
8
|
+
* presentation/launch-context only: workspace DATA access stays enforced server-side per call.
|
|
9
|
+
* @structure WorkspaceApps
|
|
10
|
+
* @usage import { WorkspaceApps } from '/views/profile/organisms/workspace-apps.js';
|
|
11
|
+
* @version-history
|
|
12
|
+
* v1.0.0 — 2026-07-02 — Initial: pinned-app cards + creator/admin pin/unpin picker.
|
|
13
|
+
*/
|
|
14
|
+
import { h } from 'preact';
|
|
15
|
+
import { useState, useEffect } from 'preact/hooks';
|
|
16
|
+
import htm from 'htm';
|
|
17
|
+
const html = htm.bind(h);
|
|
18
|
+
import { t } from '/js/i18n.js';
|
|
19
|
+
import { openAppSandboxed } from '/js/app-sandbox.js';
|
|
20
|
+
import { listApps } from '/js/services/apps.js';
|
|
21
|
+
import { saveWorkspaceApps } from '/js/services/organisms.js';
|
|
22
|
+
|
|
23
|
+
/** The launch URL for a pinned app, carrying the workspace context in the fragment. */
|
|
24
|
+
function launchHref(orgId, wsId, b) {
|
|
25
|
+
return `/v1/apps/${encodeURIComponent(b.owner)}/${encodeURIComponent(b.filename)}?mode=inline`
|
|
26
|
+
+ `#aimeat-ws=${encodeURIComponent(orgId)}/${encodeURIComponent(wsId)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function WorkspaceApps({ orgId, wsId, apps, canEdit, showToast, onChanged }) {
|
|
30
|
+
const bound = Array.isArray(apps) ? apps : [];
|
|
31
|
+
const [catalog, setCatalog] = useState(null); // null = not loaded yet; [] = loaded (maybe empty)
|
|
32
|
+
const [showPicker, setShowPicker] = useState(false);
|
|
33
|
+
const [busy, setBusy] = useState(false);
|
|
34
|
+
const [q, setQ] = useState('');
|
|
35
|
+
|
|
36
|
+
// The public /v1/apps catalog resolves each binding to its card (name/description/icon) and feeds
|
|
37
|
+
// the picker. Loaded lazily — only when there is something to resolve or the picker is open.
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (bound.length === 0 && !showPicker) return undefined;
|
|
40
|
+
let cancelled = false;
|
|
41
|
+
listApps().then(a => { if (!cancelled) setCatalog(a); }).catch(() => { if (!cancelled) setCatalog([]); });
|
|
42
|
+
return () => { cancelled = true; };
|
|
43
|
+
}, [bound.length, showPicker]);
|
|
44
|
+
|
|
45
|
+
// Members with no pins and no edit rights see nothing — no empty clutter on the overview.
|
|
46
|
+
if (bound.length === 0 && !canEdit) return null;
|
|
47
|
+
|
|
48
|
+
const sameApp = (a, b) => a.owner === b.owner && a.filename === b.filename;
|
|
49
|
+
const findApp = (b) => (catalog || []).find(a => sameApp(a, b)) || null;
|
|
50
|
+
const launch = (b) => {
|
|
51
|
+
const rec = findApp(b);
|
|
52
|
+
openAppSandboxed(launchHref(orgId, wsId, b), b.label || rec?.manifest?.name || b.filename);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const save = async (next) => {
|
|
56
|
+
setBusy(true);
|
|
57
|
+
try {
|
|
58
|
+
await saveWorkspaceApps(orgId, wsId, next.map(b => ({ owner: b.owner, filename: b.filename, ...(b.label ? { label: b.label } : {}) })));
|
|
59
|
+
showToast?.(t('organisms.apps.saved') || 'Workspace apps updated', 'success');
|
|
60
|
+
onChanged?.();
|
|
61
|
+
} catch {
|
|
62
|
+
showToast?.(t('organisms.apps.saveError') || 'Could not update apps', 'error');
|
|
63
|
+
} finally { setBusy(false); }
|
|
64
|
+
};
|
|
65
|
+
const togglePin = (a) => {
|
|
66
|
+
const has = bound.some(b => sameApp(a, b));
|
|
67
|
+
save(has ? bound.filter(b => !sameApp(a, b)) : [...bound, { owner: a.owner, filename: a.filename }]);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const renderCard = (b) => {
|
|
71
|
+
const rec = findApp(b);
|
|
72
|
+
const m = rec?.manifest || {};
|
|
73
|
+
const name = b.label || m.name || b.filename;
|
|
74
|
+
const desc = (m.description || '').length > 110 ? m.description.slice(0, 110) + '…' : (m.description || '');
|
|
75
|
+
return html`
|
|
76
|
+
<div key=${b.owner + '/' + b.filename} class="pj-app-card" role="button" tabindex="0"
|
|
77
|
+
title=${t('organisms.apps.open') || 'Open'}
|
|
78
|
+
onClick=${() => launch(b)} onKeyDown=${(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); launch(b); } }}>
|
|
79
|
+
<div class="pj-app-name">${m.icon ? m.icon + ' ' : ''}${name}</div>
|
|
80
|
+
${desc ? html`<div class="pj-app-desc">${desc}</div>` : null}
|
|
81
|
+
<div class="pj-app-meta">
|
|
82
|
+
${catalog !== null && !rec ? html`<span class="pj-app-missing">${t('organisms.apps.missing') || 'Not in the catalog (removed?)'}</span>` : (m.authorDisplay || b.owner)}
|
|
83
|
+
</div>
|
|
84
|
+
</div>`;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Picker rows: pinned apps first, then the rest alphabetically; a search filters by name/owner.
|
|
88
|
+
const ql = q.trim().toLowerCase();
|
|
89
|
+
const pickerRows = (catalog || [])
|
|
90
|
+
.filter(a => !ql || [a.manifest?.name, a.manifest?.description, a.owner, a.filename].some(v => (v || '').toLowerCase().includes(ql)))
|
|
91
|
+
.sort((a, x) => {
|
|
92
|
+
const pa = bound.some(b => sameApp(a, b)) ? 0 : 1;
|
|
93
|
+
const px = bound.some(b => sameApp(x, b)) ? 0 : 1;
|
|
94
|
+
return pa !== px ? pa - px : String(a.manifest?.name || a.filename).localeCompare(String(x.manifest?.name || x.filename));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return html`
|
|
98
|
+
<div class="pj-apps-strip">
|
|
99
|
+
<div class="pj-apps-head">
|
|
100
|
+
<span class="pj-apps-title">${t('organisms.apps.title') || 'Apps'}</span>
|
|
101
|
+
<span class="pj-apps-hint">${t('organisms.apps.hint') || 'Pinned to this workspace — they open with it as context.'}</span>
|
|
102
|
+
${canEdit ? html`
|
|
103
|
+
<button class="btn-outline btn-sm ${showPicker ? 'pj-org-btn-active' : ''}" onClick=${() => setShowPicker(s => !s)}>
|
|
104
|
+
${showPicker ? (t('organisms.apps.done') || 'Done') : ('⚙ ' + (t('organisms.apps.manage') || 'Manage'))}
|
|
105
|
+
</button>` : null}
|
|
106
|
+
</div>
|
|
107
|
+
${bound.length > 0
|
|
108
|
+
? html`<div class="pj-apps-grid">${bound.map(renderCard)}</div>`
|
|
109
|
+
: html`<div class="pj-apps-empty">${(t('organisms.apps.none') || 'No apps pinned yet.')}${canEdit ? ' ' + (t('organisms.apps.noneHint') || 'Pin a published app so members can launch it from here.') : ''}</div>`}
|
|
110
|
+
${showPicker ? html`
|
|
111
|
+
<div class="pj-apps-picker">
|
|
112
|
+
<div class="section-desc">${t('organisms.apps.pickerDesc') || 'Pick published apps to show in this workspace. Pinning only adds a launch card — data access follows workspace access.'}</div>
|
|
113
|
+
<input type="text" class="input-field input-sm" placeholder=${t('organisms.apps.search') || 'Search apps…'}
|
|
114
|
+
value=${q} onInput=${e => setQ(e.target.value)} />
|
|
115
|
+
${catalog === null ? html`<div class="pj-apps-empty">…</div>`
|
|
116
|
+
: pickerRows.length === 0 ? html`<div class="pj-apps-empty">${ql ? (t('organisms.apps.noMatch') || 'No apps match.') : (t('organisms.apps.catalogEmpty') || 'No published apps on this node yet.')}</div>`
|
|
117
|
+
: pickerRows.map(a => {
|
|
118
|
+
const pinned = bound.some(b => sameApp(a, b));
|
|
119
|
+
return html`
|
|
120
|
+
<div key=${a.owner + '/' + a.filename} class="pj-apps-picker-row">
|
|
121
|
+
<span class="pj-apps-picker-name">${a.manifest?.icon ? a.manifest.icon + ' ' : ''}${a.manifest?.name || a.filename}</span>
|
|
122
|
+
<span class="pj-apps-picker-owner">${a.manifest?.authorDisplay || a.owner}</span>
|
|
123
|
+
<button class="${pinned ? 'btn-danger btn-sm' : 'btn-outline btn-sm'}" disabled=${busy} onClick=${() => togglePin(a)}>
|
|
124
|
+
${pinned ? (t('organisms.apps.unpin') || 'Unpin') : (t('organisms.apps.pin') || 'Pin')}
|
|
125
|
+
</button>
|
|
126
|
+
</div>`;
|
|
127
|
+
})}
|
|
128
|
+
</div>` : null}
|
|
129
|
+
</div>`;
|
|
130
|
+
}
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
* (ColorPicker + TAG_PALETTE → theme tokens) lets sections, documents and records be tagged with
|
|
33
33
|
* a color (left-rail accent). Section colors persist on the section object; per-item colors in a
|
|
34
34
|
* parallel meta.colors map (getAllColors/saveColors). Colors are optional and default to none.
|
|
35
|
+
* v1.8.0 — 2026-07-02 — Apps strip on the Overview: the apps pinned to this workspace (meta.apps
|
|
36
|
+
* binding) render as launch cards (WorkspaceApps), with a creator/admin pin/unpin picker.
|
|
35
37
|
*/
|
|
36
38
|
import { h } from 'preact';
|
|
37
39
|
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
|
|
@@ -60,6 +62,7 @@ import { WorkspaceComments } from '/views/profile/organisms/workspace-comments.j
|
|
|
60
62
|
import { ActivityPanel } from '/views/profile/organisms/activity-panel.js';
|
|
61
63
|
import { ParticipantsPanel } from '/views/profile/organisms/participants-panel.js';
|
|
62
64
|
import { SourcesPanel } from '/views/profile/organisms/sources-panel.js';
|
|
65
|
+
import { WorkspaceApps } from '/views/profile/organisms/workspace-apps.js';
|
|
63
66
|
|
|
64
67
|
/* ───────────────── Organism workspace (manifest-driven) ─────────────────
|
|
65
68
|
* Any organism can have a governed workspace. If it has no manifest yet, offer
|
|
@@ -1375,6 +1378,8 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
1375
1378
|
<span class="pj-ov-event-time">${relTime(e.at)}</span>
|
|
1376
1379
|
</button>`)}
|
|
1377
1380
|
</div>` : null}
|
|
1381
|
+
<${WorkspaceApps} orgId=${orgId} wsId=${wsId} apps=${ws.apps || []} canEdit=${wsCanEdit}
|
|
1382
|
+
showToast=${showToast} onChanged=${load} />
|
|
1378
1383
|
${allTypes.map(renderOvSection)}
|
|
1379
1384
|
${allTypes.length === 0 ? html`<${EmptyState} text=${t('organisms.noneYet') || 'none yet'} />` : null}
|
|
1380
1385
|
</div>`;
|
|
@@ -410,7 +410,7 @@ export default function OrganismsTab({ session, showToast, onStats }) {
|
|
|
410
410
|
<div class="pj-org-stats">
|
|
411
411
|
${isMine && wsCounts[org.id] !== undefined ? html`
|
|
412
412
|
<span class="pj-org-stat" title=${t('organisms.tabWorkspaces') || 'Workspaces'}>${'📁'} ${wsCounts[org.id]}</span>` : null}
|
|
413
|
-
<span class="pj-org-stat" title=${t('organisms.members') || 'Members'}>${'👥'} ${(org.members || []).length}</span>
|
|
413
|
+
<span class="pj-org-stat" title=${t('organisms.members') || 'Members'}>${'👥'} ${org.member_count ?? (org.members || []).length}</span>
|
|
414
414
|
<span class="pj-org-stat" title=${t('organisms.attachedAgents') || 'Attached agents'}>${'🤖'} ${(org.agentGaiis || []).length}</span>
|
|
415
415
|
${org.createdAt ? html`<span class="pj-org-stat pj-org-date" title=${t('organisms.createdAt') || 'Created'}>${fmtDate(org.createdAt)}</span>` : null}
|
|
416
416
|
</div>
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* @structure SECRETARY_ICON · SecretaryView (default) — state, effects, handlers, layout (cards in ./secretary/cards.js + ./secretary/cards-reach.js + dashboard chrome in ./secretary/dashboard.js)
|
|
12
12
|
* @usage routed at /v1/secretary by spa.html (+ portal.ts spaRoutes).
|
|
13
13
|
* @version-history
|
|
14
|
+
* v0.26.0 — 2026-07-01 — Opt-in feature: the Secretary is now gated on AIMEAT_SECRETARY_ENABLED (off by
|
|
15
|
+
* default). The header nav link is hidden and this view redirects to /v1/profile when the node has it
|
|
16
|
+
* disabled (flag read from /v1/site/header-nav `features.secretary`).
|
|
14
17
|
* v0.25.0 — 2026-07-01 — Light-vs-heavy seam: setup no longer cheap-authors content (that hallucinated).
|
|
15
18
|
* applyResult now CREATES the workspace structure and then proposes grounded Work Briefs (POST
|
|
16
19
|
* /v1/secretary/brief) the user hands to a Builder (a connected big AI via the appdev MCP tools). Drops
|
|
@@ -131,6 +134,16 @@ export const SECRETARY_ICON = html`
|
|
|
131
134
|
|
|
132
135
|
export default function SecretaryView() {
|
|
133
136
|
useViewCSS('/css/views/secretary.css');
|
|
137
|
+
// Secretary is opt-in per node (AIMEAT_SECRETARY_ENABLED). If it's disabled but this view is reached
|
|
138
|
+
// via a direct URL/bookmark (no nav link is rendered when off), bounce back to the profile.
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
let cancelled = false;
|
|
141
|
+
fetch('/v1/site/header-nav')
|
|
142
|
+
.then(r => r.json())
|
|
143
|
+
.then(d => { if (!cancelled && d?.ok && d.data?.features && d.data.features.secretary === false) window.location.replace('/v1/profile'); })
|
|
144
|
+
.catch(() => {});
|
|
145
|
+
return () => { cancelled = true; };
|
|
146
|
+
}, []);
|
|
134
147
|
const { showToast, ToastContainer } = useToast();
|
|
135
148
|
const [secretary, setSecretary] = useState(undefined); // undefined=loading, null=not provisioned
|
|
136
149
|
const [hasOpenRouterKey, setHasOpenRouterKey] = useState(false); // owner has an OpenRouter key configured
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file archive-stale-versions.ts
|
|
4
|
+
* @description One-time, REVERSIBLE cleanup of accumulated workspace-record version bloat. Publishing a
|
|
5
|
+
* workspace record used to append a new `.version.N` on every publish with no change-guard, so a
|
|
6
|
+
* contract agent that re-published the same draft each poll cycle piled up dozens of byte-identical
|
|
7
|
+
* versions per record (concentrated in request queues: mail/research/moodboard/image_requests). The
|
|
8
|
+
* publish path is now guarded (see mcp/workspaces.ts + routes/organisms.ts), so this stops GROWING;
|
|
9
|
+
* this script cleans up what already accrued.
|
|
10
|
+
*
|
|
11
|
+
* It does NOT delete: it flags redundant version rows `archived = 1` (the same reversible soft-archive
|
|
12
|
+
* the organism archive uses) under a shared `archivedRoot` so the whole batch can be restored with
|
|
13
|
+
* --unarchive. A version row is redundant when it is byte-identical to the version immediately before
|
|
14
|
+
* it (by N) — a pure republish that recorded no real change. Every genuine state transition, the
|
|
15
|
+
* first version, and the highest-numbered version (so publish's next-version scan is unaffected) are
|
|
16
|
+
* ALWAYS kept. Document/record spaces that version legitimately have no consecutive duplicates, so
|
|
17
|
+
* nothing there is touched.
|
|
18
|
+
*
|
|
19
|
+
* Read-only by default. `--from-export=<json>` analyses an exported memory dump with no DB at all.
|
|
20
|
+
* Detects the storage provider from .env for the live scan / apply.
|
|
21
|
+
* @usage
|
|
22
|
+
* pnpm versions:stats # live dry-run: what WOULD be archived (per namespace)
|
|
23
|
+
* pnpm versions:stats -- --from-export=dump.json # analyse an export JSON, no DB touched
|
|
24
|
+
* pnpm versions:archive # apply the archive (asks to confirm)
|
|
25
|
+
* pnpm versions:archive -- --force # apply without the prompt
|
|
26
|
+
* pnpm versions:stats -- --unarchive # preview restore; add --apply to restore
|
|
27
|
+
* pnpm versions:archive -- --unarchive --force # restore the whole archived batch
|
|
28
|
+
* @version-history
|
|
29
|
+
* v1.0.0 -- 2026-07-01 -- Initial: reversible consecutive-identical version dedupe (sqlite + mongodb),
|
|
30
|
+
* plus a DB-free --from-export analyser.
|
|
31
|
+
*/
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=archive-stale-versions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-stale-versions.d.ts","sourceRoot":"","sources":["../../scripts/archive-stale-versions.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG"}
|