feat: scope activity and browser links by durable workspace

This commit is contained in:
2026-07-12 20:39:03 +08:00
parent fca9fff4b4
commit 6b556c59d9
8 changed files with 75 additions and 2 deletions
+35 -1
View File
@@ -577,7 +577,7 @@ func (a *App) recordActivityProviderEvent(event events.Event) {
if _, err := a.requirePluginAccess(provider.PluginID, "storage.namespace"); err != nil {
continue
}
if err := a.appendActivityEvent(provider.PluginID, activityFromEvent(event)); err != nil {
if err := a.appendActivityEvent(provider.PluginID, a.activityFromEvent(event)); err != nil {
log.Printf("[api] activity provider %s failed to record %s: %v", provider.PluginID, event.Name, err)
}
}
@@ -622,6 +622,24 @@ func activityFromEvent(event events.Event) map[string]interface{} {
}
}
func (a *App) activityFromEvent(event events.Event) map[string]interface{} {
activity := activityFromEvent(event)
workspaceRoot := firstPayloadText(activity, "workspaceRootPath")
if workspaceRoot == "" || a == nil || a.workspace == nil {
activity["sessionScope"] = map[string]interface{}{"kind": "unassigned"}
return activity
}
identity, err := a.workspace.GetWorkspaceIdentity(workspaceRoot)
if err != nil {
activity["sessionScope"] = map[string]interface{}{"kind": "unassigned"}
return activity
}
activity["workspaceId"] = identity.WorkspaceID
activity["workspaceRootPath"] = identity.RootPath
activity["sessionScope"] = map[string]interface{}{"kind": "workspace", "workspaceId": identity.WorkspaceID}
return activity
}
func eventPayloadMap(payload interface{}) map[string]interface{} {
switch value := payload.(type) {
case map[string]interface{}:
@@ -1498,6 +1516,22 @@ func (a *App) OpenVaultPathExternal(pluginID, relativePath string) string {
return ""
}
// OpenExternalURL opens an HTTP(S) URL through the platform browser opener.
// This deliberately bypasses OS file associations for InternetShortcut files.
func (a *App) OpenExternalURL(pluginID, rawURL string) string {
if _, err := a.requirePluginAccess(pluginID, "files.openExternal"); err != nil {
return err.Error()
}
parsed, err := url.ParseRequestURI(strings.TrimSpace(rawURL))
if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") {
return "invalid HTTP(S) URL"
}
if err := a.externalOpenService().OpenPath(parsed.String()); err != nil {
return err.Error()
}
return ""
}
// ShowVaultPathInFolder reveals a vault-relative file or folder in the OS file manager.
func (a *App) ShowVaultPathInFolder(pluginID, relativePath string) string {
if _, err := a.requirePluginAccess(pluginID, "files.openExternal"); err != nil {
+18
View File
@@ -823,6 +823,24 @@ func TestBrowserInboxWorkspaceReferenceSurvivesRenameAndTrash(t *testing.T) {
}
}
func TestOpenExternalURLUsesBrowserOpenService(t *testing.T) {
app, _ := newFilesTestApp(t, []string{"files.openExternal"})
opened := ""
app.externalOpen = newTestExternalOpenService(func(path string) error {
opened = path
return nil
})
if errStr := app.OpenExternalURL("files.plugin", "https://example.com/path"); errStr != "" {
t.Fatalf("OpenExternalURL: %s", errStr)
}
if opened != "https://example.com/path" {
t.Fatalf("opened = %q, want URL", opened)
}
if errStr := app.OpenExternalURL("files.plugin", "ftp://example.com"); errStr == "" {
t.Fatal("OpenExternalURL accepted unsupported URL scheme")
}
}
func TestBrowserInboxRejectsCaptureWithoutOpenVault(t *testing.T) {
v := vault.NewVault(nil)
app := &App{