@stoneforge/quarry 0.1.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/LICENSE +13 -0
- package/README.md +160 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +8 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/quarry-api.d.ts +268 -0
- package/dist/api/quarry-api.d.ts.map +1 -0
- package/dist/api/quarry-api.js +3905 -0
- package/dist/api/quarry-api.js.map +1 -0
- package/dist/api/types.d.ts +1359 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +204 -0
- package/dist/api/types.js.map +1 -0
- package/dist/bin/sf.d.ts +3 -0
- package/dist/bin/sf.d.ts.map +1 -0
- package/dist/bin/sf.js +9 -0
- package/dist/bin/sf.js.map +1 -0
- package/dist/cli/commands/admin.d.ts +11 -0
- package/dist/cli/commands/admin.d.ts.map +1 -0
- package/dist/cli/commands/admin.js +465 -0
- package/dist/cli/commands/admin.js.map +1 -0
- package/dist/cli/commands/alias.d.ts +8 -0
- package/dist/cli/commands/alias.d.ts.map +1 -0
- package/dist/cli/commands/alias.js +70 -0
- package/dist/cli/commands/alias.js.map +1 -0
- package/dist/cli/commands/channel.d.ts +13 -0
- package/dist/cli/commands/channel.d.ts.map +1 -0
- package/dist/cli/commands/channel.js +680 -0
- package/dist/cli/commands/channel.js.map +1 -0
- package/dist/cli/commands/completion.d.ts +8 -0
- package/dist/cli/commands/completion.d.ts.map +1 -0
- package/dist/cli/commands/completion.js +87 -0
- package/dist/cli/commands/completion.js.map +1 -0
- package/dist/cli/commands/config.d.ts +12 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +242 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/crud.d.ts +64 -0
- package/dist/cli/commands/crud.d.ts.map +1 -0
- package/dist/cli/commands/crud.js +805 -0
- package/dist/cli/commands/crud.js.map +1 -0
- package/dist/cli/commands/dep.d.ts +16 -0
- package/dist/cli/commands/dep.d.ts.map +1 -0
- package/dist/cli/commands/dep.js +499 -0
- package/dist/cli/commands/dep.js.map +1 -0
- package/dist/cli/commands/document.d.ts +12 -0
- package/dist/cli/commands/document.d.ts.map +1 -0
- package/dist/cli/commands/document.js +1039 -0
- package/dist/cli/commands/document.js.map +1 -0
- package/dist/cli/commands/embeddings.d.ts +12 -0
- package/dist/cli/commands/embeddings.d.ts.map +1 -0
- package/dist/cli/commands/embeddings.js +273 -0
- package/dist/cli/commands/embeddings.js.map +1 -0
- package/dist/cli/commands/entity.d.ts +16 -0
- package/dist/cli/commands/entity.d.ts.map +1 -0
- package/dist/cli/commands/entity.js +522 -0
- package/dist/cli/commands/entity.js.map +1 -0
- package/dist/cli/commands/gc.d.ts +10 -0
- package/dist/cli/commands/gc.d.ts.map +1 -0
- package/dist/cli/commands/gc.js +257 -0
- package/dist/cli/commands/gc.js.map +1 -0
- package/dist/cli/commands/help.d.ts +11 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +169 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/history.d.ts +9 -0
- package/dist/cli/commands/history.d.ts.map +1 -0
- package/dist/cli/commands/history.js +160 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/identity.d.ts +18 -0
- package/dist/cli/commands/identity.d.ts.map +1 -0
- package/dist/cli/commands/identity.js +698 -0
- package/dist/cli/commands/identity.js.map +1 -0
- package/dist/cli/commands/inbox.d.ts +20 -0
- package/dist/cli/commands/inbox.d.ts.map +1 -0
- package/dist/cli/commands/inbox.js +493 -0
- package/dist/cli/commands/inbox.js.map +1 -0
- package/dist/cli/commands/init.d.ts +20 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +144 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/install.d.ts +9 -0
- package/dist/cli/commands/install.d.ts.map +1 -0
- package/dist/cli/commands/install.js +200 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/library.d.ts +12 -0
- package/dist/cli/commands/library.d.ts.map +1 -0
- package/dist/cli/commands/library.js +665 -0
- package/dist/cli/commands/library.js.map +1 -0
- package/dist/cli/commands/message.d.ts +11 -0
- package/dist/cli/commands/message.d.ts.map +1 -0
- package/dist/cli/commands/message.js +608 -0
- package/dist/cli/commands/message.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +17 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +698 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/playbook.d.ts +12 -0
- package/dist/cli/commands/playbook.d.ts.map +1 -0
- package/dist/cli/commands/playbook.js +730 -0
- package/dist/cli/commands/playbook.js.map +1 -0
- package/dist/cli/commands/reset.d.ts +12 -0
- package/dist/cli/commands/reset.d.ts.map +1 -0
- package/dist/cli/commands/reset.js +306 -0
- package/dist/cli/commands/reset.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +11 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +106 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +8 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +82 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +14 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +370 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/task.d.ts +25 -0
- package/dist/cli/commands/task.d.ts.map +1 -0
- package/dist/cli/commands/task.js +1153 -0
- package/dist/cli/commands/task.js.map +1 -0
- package/dist/cli/commands/team.d.ts +13 -0
- package/dist/cli/commands/team.d.ts.map +1 -0
- package/dist/cli/commands/team.js +471 -0
- package/dist/cli/commands/team.js.map +1 -0
- package/dist/cli/commands/workflow.d.ts +16 -0
- package/dist/cli/commands/workflow.d.ts.map +1 -0
- package/dist/cli/commands/workflow.js +753 -0
- package/dist/cli/commands/workflow.js.map +1 -0
- package/dist/cli/completion.d.ts +28 -0
- package/dist/cli/completion.d.ts.map +1 -0
- package/dist/cli/completion.js +295 -0
- package/dist/cli/completion.js.map +1 -0
- package/dist/cli/db.d.ts +38 -0
- package/dist/cli/db.d.ts.map +1 -0
- package/dist/cli/db.js +90 -0
- package/dist/cli/db.js.map +1 -0
- package/dist/cli/formatter.d.ts +87 -0
- package/dist/cli/formatter.d.ts.map +1 -0
- package/dist/cli/formatter.js +464 -0
- package/dist/cli/formatter.js.map +1 -0
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +38 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/parser.d.ts +45 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +256 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/cli/plugin-loader.d.ts +39 -0
- package/dist/cli/plugin-loader.d.ts.map +1 -0
- package/dist/cli/plugin-loader.js +165 -0
- package/dist/cli/plugin-loader.js.map +1 -0
- package/dist/cli/plugin-registry.d.ts +50 -0
- package/dist/cli/plugin-registry.d.ts.map +1 -0
- package/dist/cli/plugin-registry.js +206 -0
- package/dist/cli/plugin-registry.js.map +1 -0
- package/dist/cli/plugin-types.d.ts +106 -0
- package/dist/cli/plugin-types.d.ts.map +1 -0
- package/dist/cli/plugin-types.js +103 -0
- package/dist/cli/plugin-types.js.map +1 -0
- package/dist/cli/runner.d.ts +35 -0
- package/dist/cli/runner.d.ts.map +1 -0
- package/dist/cli/runner.js +340 -0
- package/dist/cli/runner.js.map +1 -0
- package/dist/cli/suggest.d.ts +15 -0
- package/dist/cli/suggest.d.ts.map +1 -0
- package/dist/cli/suggest.js +49 -0
- package/dist/cli/suggest.js.map +1 -0
- package/dist/cli/types.d.ts +138 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +63 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/config/config.d.ts +86 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +348 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/defaults.d.ts +66 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +114 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/duration.d.ts +75 -0
- package/dist/config/duration.d.ts.map +1 -0
- package/dist/config/duration.js +190 -0
- package/dist/config/duration.js.map +1 -0
- package/dist/config/env.d.ts +67 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +207 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/file.d.ts +97 -0
- package/dist/config/file.d.ts.map +1 -0
- package/dist/config/file.js +365 -0
- package/dist/config/file.js.map +1 -0
- package/dist/config/index.d.ts +35 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +41 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/merge.d.ts +53 -0
- package/dist/config/merge.d.ts.map +1 -0
- package/dist/config/merge.js +226 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/types.d.ts +257 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +72 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validation.d.ts +55 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +251 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/http/index.d.ts +8 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +12 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/sync-handlers.d.ts +162 -0
- package/dist/http/sync-handlers.d.ts.map +1 -0
- package/dist/http/sync-handlers.js +271 -0
- package/dist/http/sync-handlers.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/server/index.d.ts +34 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3329 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/static.d.ts +18 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +71 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/ws/broadcaster.d.ts +8 -0
- package/dist/server/ws/broadcaster.d.ts.map +1 -0
- package/dist/server/ws/broadcaster.js +7 -0
- package/dist/server/ws/broadcaster.js.map +1 -0
- package/dist/server/ws/handler.d.ts +55 -0
- package/dist/server/ws/handler.d.ts.map +1 -0
- package/dist/server/ws/handler.js +160 -0
- package/dist/server/ws/handler.js.map +1 -0
- package/dist/services/blocked-cache.d.ts +297 -0
- package/dist/services/blocked-cache.d.ts.map +1 -0
- package/dist/services/blocked-cache.js +755 -0
- package/dist/services/blocked-cache.js.map +1 -0
- package/dist/services/dependency.d.ts +205 -0
- package/dist/services/dependency.d.ts.map +1 -0
- package/dist/services/dependency.js +566 -0
- package/dist/services/dependency.js.map +1 -0
- package/dist/services/embeddings/fusion.d.ts +33 -0
- package/dist/services/embeddings/fusion.d.ts.map +1 -0
- package/dist/services/embeddings/fusion.js +34 -0
- package/dist/services/embeddings/fusion.js.map +1 -0
- package/dist/services/embeddings/index.d.ts +12 -0
- package/dist/services/embeddings/index.d.ts.map +1 -0
- package/dist/services/embeddings/index.js +10 -0
- package/dist/services/embeddings/index.js.map +1 -0
- package/dist/services/embeddings/local-provider.d.ts +31 -0
- package/dist/services/embeddings/local-provider.d.ts.map +1 -0
- package/dist/services/embeddings/local-provider.js +80 -0
- package/dist/services/embeddings/local-provider.js.map +1 -0
- package/dist/services/embeddings/service.d.ts +76 -0
- package/dist/services/embeddings/service.d.ts.map +1 -0
- package/dist/services/embeddings/service.js +153 -0
- package/dist/services/embeddings/service.js.map +1 -0
- package/dist/services/embeddings/types.d.ts +70 -0
- package/dist/services/embeddings/types.d.ts.map +1 -0
- package/dist/services/embeddings/types.js +8 -0
- package/dist/services/embeddings/types.js.map +1 -0
- package/dist/services/id-length-cache.d.ts +156 -0
- package/dist/services/id-length-cache.d.ts.map +1 -0
- package/dist/services/id-length-cache.js +197 -0
- package/dist/services/id-length-cache.js.map +1 -0
- package/dist/services/inbox.d.ts +147 -0
- package/dist/services/inbox.d.ts.map +1 -0
- package/dist/services/inbox.js +428 -0
- package/dist/services/inbox.js.map +1 -0
- package/dist/services/index.d.ts +10 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +10 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/priority-service.d.ts +145 -0
- package/dist/services/priority-service.d.ts.map +1 -0
- package/dist/services/priority-service.js +272 -0
- package/dist/services/priority-service.js.map +1 -0
- package/dist/services/search-utils.d.ts +47 -0
- package/dist/services/search-utils.d.ts.map +1 -0
- package/dist/services/search-utils.js +83 -0
- package/dist/services/search-utils.js.map +1 -0
- package/dist/sync/hash.d.ts +48 -0
- package/dist/sync/hash.d.ts.map +1 -0
- package/dist/sync/hash.js +136 -0
- package/dist/sync/hash.js.map +1 -0
- package/dist/sync/index.d.ts +11 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +16 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/merge.d.ts +80 -0
- package/dist/sync/merge.d.ts.map +1 -0
- package/dist/sync/merge.js +310 -0
- package/dist/sync/merge.js.map +1 -0
- package/dist/sync/serialization.d.ts +132 -0
- package/dist/sync/serialization.d.ts.map +1 -0
- package/dist/sync/serialization.js +306 -0
- package/dist/sync/serialization.js.map +1 -0
- package/dist/sync/service.d.ts +102 -0
- package/dist/sync/service.d.ts.map +1 -0
- package/dist/sync/service.js +493 -0
- package/dist/sync/service.js.map +1 -0
- package/dist/sync/types.d.ts +275 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +76 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/systems/identity.d.ts +479 -0
- package/dist/systems/identity.d.ts.map +1 -0
- package/dist/systems/identity.js +817 -0
- package/dist/systems/identity.js.map +1 -0
- package/dist/systems/index.d.ts +8 -0
- package/dist/systems/index.d.ts.map +1 -0
- package/dist/systems/index.js +29 -0
- package/dist/systems/index.js.map +1 -0
- package/package.json +121 -0
- package/web/assets/charts-vendor-D1YcbGux.js +55 -0
- package/web/assets/dnd-vendor-DmxE-_ZH.js +5 -0
- package/web/assets/editor-vendor-BxraAWts.js +279 -0
- package/web/assets/index-B77vv208.js +341 -0
- package/web/assets/index-CF_XnVLh.css +1 -0
- package/web/assets/router-vendor-BCKpRBrB.js +41 -0
- package/web/assets/ui-vendor-DUahGnbT.js +45 -0
- package/web/assets/utils-vendor-CfYKiENT.js +813 -0
- package/web/favicon.ico +0 -0
- package/web/index.html +23 -0
- package/web/logo.png +0 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel Commands - Collection command interface for channels
|
|
3
|
+
*
|
|
4
|
+
* Provides CLI commands for channel operations:
|
|
5
|
+
* - channel create: Create a new channel
|
|
6
|
+
* - channel join: Join a channel
|
|
7
|
+
* - channel leave: Leave a channel
|
|
8
|
+
* - channel list: List channels
|
|
9
|
+
* - channel members: List channel members
|
|
10
|
+
*/
|
|
11
|
+
import { success, failure, ExitCode } from '../types.js';
|
|
12
|
+
import { getFormatter, getOutputMode } from '../formatter.js';
|
|
13
|
+
import { createGroupChannel, createDirectChannel, ChannelTypeValue, VisibilityValue, JoinPolicyValue, isMember, } from '@stoneforge/core';
|
|
14
|
+
import { suggestCommands } from '../suggest.js';
|
|
15
|
+
import { resolveActor, createAPI } from '../db.js';
|
|
16
|
+
const channelCreateOptions = [
|
|
17
|
+
{
|
|
18
|
+
name: 'name',
|
|
19
|
+
short: 'n',
|
|
20
|
+
description: 'Channel name (required for group channels)',
|
|
21
|
+
hasValue: true,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'description',
|
|
25
|
+
short: 'D',
|
|
26
|
+
description: 'Channel description',
|
|
27
|
+
hasValue: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'type',
|
|
31
|
+
short: 't',
|
|
32
|
+
description: 'Channel type: group (default) or direct',
|
|
33
|
+
hasValue: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'visibility',
|
|
37
|
+
short: 'V',
|
|
38
|
+
description: 'Visibility: public or private (default)',
|
|
39
|
+
hasValue: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'policy',
|
|
43
|
+
short: 'p',
|
|
44
|
+
description: 'Join policy: open, invite-only (default), or request',
|
|
45
|
+
hasValue: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'member',
|
|
49
|
+
short: 'm',
|
|
50
|
+
description: 'Add member (can be repeated)',
|
|
51
|
+
hasValue: true,
|
|
52
|
+
array: true,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'direct',
|
|
56
|
+
short: 'd',
|
|
57
|
+
description: 'Create direct channel with entity (for --type direct)',
|
|
58
|
+
hasValue: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'tag',
|
|
62
|
+
description: 'Add tag (can be repeated)',
|
|
63
|
+
hasValue: true,
|
|
64
|
+
array: true,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
async function channelCreateHandler(_args, options) {
|
|
68
|
+
const channelType = (options.type || 'group');
|
|
69
|
+
if (channelType !== 'group' && channelType !== 'direct') {
|
|
70
|
+
return failure(`Invalid channel type: ${channelType}. Must be 'group' or 'direct'`, ExitCode.VALIDATION);
|
|
71
|
+
}
|
|
72
|
+
const { api, error } = createAPI(options, true);
|
|
73
|
+
if (error) {
|
|
74
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const actor = resolveActor(options);
|
|
78
|
+
// Handle tags
|
|
79
|
+
let tags;
|
|
80
|
+
if (options.tag) {
|
|
81
|
+
tags = Array.isArray(options.tag) ? options.tag : [options.tag];
|
|
82
|
+
}
|
|
83
|
+
let channel;
|
|
84
|
+
if (channelType === 'direct') {
|
|
85
|
+
if (!options.direct) {
|
|
86
|
+
return failure('--direct <entity-id> is required for direct channels', ExitCode.INVALID_ARGUMENTS);
|
|
87
|
+
}
|
|
88
|
+
const input = {
|
|
89
|
+
entityA: actor,
|
|
90
|
+
entityB: options.direct,
|
|
91
|
+
createdBy: actor,
|
|
92
|
+
...(options.description && { description: options.description }),
|
|
93
|
+
...(tags && { tags }),
|
|
94
|
+
};
|
|
95
|
+
channel = await createDirectChannel(input);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
if (!options.name) {
|
|
99
|
+
return failure('--name is required for group channels', ExitCode.INVALID_ARGUMENTS);
|
|
100
|
+
}
|
|
101
|
+
// Validate visibility
|
|
102
|
+
const visibility = (options.visibility || 'private');
|
|
103
|
+
if (!Object.values(VisibilityValue).includes(visibility)) {
|
|
104
|
+
return failure(`Invalid visibility: ${visibility}. Must be 'public' or 'private'`, ExitCode.VALIDATION);
|
|
105
|
+
}
|
|
106
|
+
// Validate join policy
|
|
107
|
+
const joinPolicy = (options.policy || 'invite-only');
|
|
108
|
+
if (!Object.values(JoinPolicyValue).includes(joinPolicy)) {
|
|
109
|
+
return failure(`Invalid join policy: ${joinPolicy}. Must be 'open', 'invite-only', or 'request'`, ExitCode.VALIDATION);
|
|
110
|
+
}
|
|
111
|
+
// Parse members
|
|
112
|
+
let members;
|
|
113
|
+
if (options.member) {
|
|
114
|
+
members = (Array.isArray(options.member) ? options.member : [options.member]);
|
|
115
|
+
}
|
|
116
|
+
const input = {
|
|
117
|
+
name: options.name,
|
|
118
|
+
createdBy: actor,
|
|
119
|
+
description: options.description ?? null,
|
|
120
|
+
visibility,
|
|
121
|
+
joinPolicy,
|
|
122
|
+
...(members && { members }),
|
|
123
|
+
...(tags && { tags }),
|
|
124
|
+
};
|
|
125
|
+
channel = await createGroupChannel(input);
|
|
126
|
+
}
|
|
127
|
+
const created = await api.create(channel);
|
|
128
|
+
const mode = getOutputMode(options);
|
|
129
|
+
if (mode === 'quiet') {
|
|
130
|
+
return success(created.id);
|
|
131
|
+
}
|
|
132
|
+
return success(created, `Created ${channelType} channel ${created.id}`);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
136
|
+
return failure(`Failed to create channel: ${message}`, ExitCode.GENERAL_ERROR);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const channelCreateCommand = {
|
|
140
|
+
name: 'create',
|
|
141
|
+
description: 'Create a new channel',
|
|
142
|
+
usage: 'sf channel create [options]',
|
|
143
|
+
help: `Create a new channel for communication.
|
|
144
|
+
|
|
145
|
+
Options:
|
|
146
|
+
-n, --name <name> Channel name (required for group)
|
|
147
|
+
-D, --description <desc> Channel description
|
|
148
|
+
-t, --type <type> Type: group (default) or direct
|
|
149
|
+
-V, --visibility <vis> Visibility: public or private (default)
|
|
150
|
+
-p, --policy <policy> Join policy: open, invite-only (default), request
|
|
151
|
+
-m, --member <entity> Add member (can be repeated)
|
|
152
|
+
-d, --direct <entity> Create direct channel with entity
|
|
153
|
+
--tag <tag> Add tag (can be repeated)
|
|
154
|
+
|
|
155
|
+
Examples:
|
|
156
|
+
sf channel create --name general --description "General discussion"
|
|
157
|
+
sf channel create -n announcements -V public -p open
|
|
158
|
+
sf channel create --type direct --direct el-user123
|
|
159
|
+
sf channel create -n team -m el-user1 -m el-user2`,
|
|
160
|
+
options: channelCreateOptions,
|
|
161
|
+
handler: channelCreateHandler,
|
|
162
|
+
};
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Channel Join Command
|
|
165
|
+
// ============================================================================
|
|
166
|
+
async function channelJoinHandler(args, options) {
|
|
167
|
+
const [id] = args;
|
|
168
|
+
if (!id) {
|
|
169
|
+
return failure('Usage: sf channel join <id>\nExample: sf channel join el-abc123', ExitCode.INVALID_ARGUMENTS);
|
|
170
|
+
}
|
|
171
|
+
const { api, error } = createAPI(options);
|
|
172
|
+
if (error) {
|
|
173
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const actor = resolveActor(options);
|
|
177
|
+
const channel = await api.get(id);
|
|
178
|
+
if (!channel) {
|
|
179
|
+
return failure(`Channel not found: ${id}`, ExitCode.NOT_FOUND);
|
|
180
|
+
}
|
|
181
|
+
if (channel.type !== 'channel') {
|
|
182
|
+
return failure(`Element ${id} is not a channel (type: ${channel.type})`, ExitCode.VALIDATION);
|
|
183
|
+
}
|
|
184
|
+
if (channel.channelType === ChannelTypeValue.DIRECT) {
|
|
185
|
+
return failure('Cannot join a direct channel', ExitCode.VALIDATION);
|
|
186
|
+
}
|
|
187
|
+
if (isMember(channel, actor)) {
|
|
188
|
+
return success(channel, `Already a member of channel ${id}`);
|
|
189
|
+
}
|
|
190
|
+
// Check join policy
|
|
191
|
+
if (channel.permissions.joinPolicy === JoinPolicyValue.INVITE_ONLY) {
|
|
192
|
+
return failure('Channel is invite-only. Ask a moderator to add you.', ExitCode.VALIDATION);
|
|
193
|
+
}
|
|
194
|
+
if (channel.permissions.joinPolicy === JoinPolicyValue.OPEN &&
|
|
195
|
+
channel.permissions.visibility !== VisibilityValue.PUBLIC) {
|
|
196
|
+
return failure('Channel is private. Cannot join without invitation.', ExitCode.VALIDATION);
|
|
197
|
+
}
|
|
198
|
+
// Add actor to members
|
|
199
|
+
const newMembers = [...channel.members, actor];
|
|
200
|
+
const updated = await api.update(id, { members: newMembers }, { actor });
|
|
201
|
+
return success(updated, `Joined channel ${id}`);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
205
|
+
return failure(`Failed to join channel: ${message}`, ExitCode.GENERAL_ERROR);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const channelJoinCommand = {
|
|
209
|
+
name: 'join',
|
|
210
|
+
description: 'Join a channel',
|
|
211
|
+
usage: 'sf channel join <id>',
|
|
212
|
+
help: `Join a channel.
|
|
213
|
+
|
|
214
|
+
Only works for group channels with open or request join policy.
|
|
215
|
+
Direct channels and invite-only channels cannot be joined directly.
|
|
216
|
+
|
|
217
|
+
Arguments:
|
|
218
|
+
id Channel identifier
|
|
219
|
+
|
|
220
|
+
Examples:
|
|
221
|
+
sf channel join el-abc123`,
|
|
222
|
+
handler: channelJoinHandler,
|
|
223
|
+
};
|
|
224
|
+
// ============================================================================
|
|
225
|
+
// Channel Leave Command
|
|
226
|
+
// ============================================================================
|
|
227
|
+
async function channelLeaveHandler(args, options) {
|
|
228
|
+
const [id] = args;
|
|
229
|
+
if (!id) {
|
|
230
|
+
return failure('Usage: sf channel leave <id>\nExample: sf channel leave el-abc123', ExitCode.INVALID_ARGUMENTS);
|
|
231
|
+
}
|
|
232
|
+
const { api, error } = createAPI(options);
|
|
233
|
+
if (error) {
|
|
234
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const actor = resolveActor(options);
|
|
238
|
+
const result = await api.leaveChannel(id, actor);
|
|
239
|
+
return success(result.channel, `Left channel ${id}`);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
// Handle specific error cases with user-friendly messages
|
|
243
|
+
if (err instanceof Error) {
|
|
244
|
+
if (err.message.includes('not found')) {
|
|
245
|
+
return failure(`Channel not found: ${id}`, ExitCode.NOT_FOUND);
|
|
246
|
+
}
|
|
247
|
+
if (err.message.includes('not a channel')) {
|
|
248
|
+
return failure(`Element ${id} is not a channel`, ExitCode.VALIDATION);
|
|
249
|
+
}
|
|
250
|
+
if (err.message.includes('Cannot leave a direct channel')) {
|
|
251
|
+
return failure('Cannot leave a direct channel', ExitCode.VALIDATION);
|
|
252
|
+
}
|
|
253
|
+
if (err.message.includes('not a member')) {
|
|
254
|
+
// Not an error - just inform the user
|
|
255
|
+
const channel = await api.get(id);
|
|
256
|
+
return success(channel, `Not a member of channel ${id}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
260
|
+
return failure(`Failed to leave channel: ${message}`, ExitCode.GENERAL_ERROR);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const channelLeaveCommand = {
|
|
264
|
+
name: 'leave',
|
|
265
|
+
description: 'Leave a channel',
|
|
266
|
+
usage: 'sf channel leave <id>',
|
|
267
|
+
help: `Leave a channel.
|
|
268
|
+
|
|
269
|
+
Only works for group channels. Direct channels cannot be left.
|
|
270
|
+
|
|
271
|
+
Arguments:
|
|
272
|
+
id Channel identifier
|
|
273
|
+
|
|
274
|
+
Examples:
|
|
275
|
+
sf channel leave el-abc123`,
|
|
276
|
+
handler: channelLeaveHandler,
|
|
277
|
+
};
|
|
278
|
+
const channelListOptions = [
|
|
279
|
+
{
|
|
280
|
+
name: 'type',
|
|
281
|
+
short: 't',
|
|
282
|
+
description: 'Filter by type: group or direct',
|
|
283
|
+
hasValue: true,
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: 'member',
|
|
287
|
+
short: 'm',
|
|
288
|
+
description: 'Filter by member entity',
|
|
289
|
+
hasValue: true,
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'limit',
|
|
293
|
+
short: 'l',
|
|
294
|
+
description: 'Maximum number of results',
|
|
295
|
+
hasValue: true,
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
async function channelListHandler(_args, options) {
|
|
299
|
+
const { api, error } = createAPI(options);
|
|
300
|
+
if (error) {
|
|
301
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
// Build filter
|
|
305
|
+
const filter = {
|
|
306
|
+
type: 'channel',
|
|
307
|
+
};
|
|
308
|
+
// Limit
|
|
309
|
+
if (options.limit) {
|
|
310
|
+
const limit = parseInt(options.limit, 10);
|
|
311
|
+
if (isNaN(limit) || limit < 1) {
|
|
312
|
+
return failure('Limit must be a positive number', ExitCode.VALIDATION);
|
|
313
|
+
}
|
|
314
|
+
filter.limit = limit;
|
|
315
|
+
}
|
|
316
|
+
const result = await api.listPaginated(filter);
|
|
317
|
+
// Post-filter
|
|
318
|
+
let items = result.items;
|
|
319
|
+
// Type filter
|
|
320
|
+
if (options.type) {
|
|
321
|
+
if (options.type !== 'group' && options.type !== 'direct') {
|
|
322
|
+
return failure(`Invalid type: ${options.type}. Must be 'group' or 'direct'`, ExitCode.VALIDATION);
|
|
323
|
+
}
|
|
324
|
+
items = items.filter((c) => c.channelType === options.type);
|
|
325
|
+
}
|
|
326
|
+
// Member filter
|
|
327
|
+
if (options.member) {
|
|
328
|
+
items = items.filter((c) => c.members.includes(options.member));
|
|
329
|
+
}
|
|
330
|
+
const mode = getOutputMode(options);
|
|
331
|
+
const formatter = getFormatter(mode);
|
|
332
|
+
if (mode === 'json') {
|
|
333
|
+
return success(items);
|
|
334
|
+
}
|
|
335
|
+
if (mode === 'quiet') {
|
|
336
|
+
return success(items.map((c) => c.id).join('\n'));
|
|
337
|
+
}
|
|
338
|
+
if (items.length === 0) {
|
|
339
|
+
return success(null, 'No channels found');
|
|
340
|
+
}
|
|
341
|
+
// Build table
|
|
342
|
+
const headers = ['ID', 'NAME', 'TYPE', 'MEMBERS', 'VISIBILITY', 'DESCRIPTION', 'CREATED'];
|
|
343
|
+
const rows = items.map((c) => {
|
|
344
|
+
const desc = c.description ?? '';
|
|
345
|
+
const truncDesc = desc.length > 30 ? desc.substring(0, 27) + '...' : desc;
|
|
346
|
+
return [
|
|
347
|
+
c.id,
|
|
348
|
+
c.name.length > 25 ? c.name.substring(0, 22) + '...' : c.name,
|
|
349
|
+
c.channelType,
|
|
350
|
+
String(c.members.length),
|
|
351
|
+
c.permissions.visibility,
|
|
352
|
+
truncDesc,
|
|
353
|
+
c.createdAt.split('T')[0],
|
|
354
|
+
];
|
|
355
|
+
});
|
|
356
|
+
const table = formatter.table(headers, rows);
|
|
357
|
+
const summary = `\nShowing ${items.length} of ${result.total} channels`;
|
|
358
|
+
return success(items, table + summary);
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
362
|
+
return failure(`Failed to list channels: ${message}`, ExitCode.GENERAL_ERROR);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const channelListCommand = {
|
|
366
|
+
name: 'list',
|
|
367
|
+
description: 'List channels',
|
|
368
|
+
usage: 'sf channel list [options]',
|
|
369
|
+
help: `List channels with optional filtering.
|
|
370
|
+
|
|
371
|
+
Options:
|
|
372
|
+
-t, --type <type> Filter by type: group or direct
|
|
373
|
+
-m, --member <entity> Filter by member entity
|
|
374
|
+
-l, --limit <n> Maximum results
|
|
375
|
+
|
|
376
|
+
Examples:
|
|
377
|
+
sf channel list
|
|
378
|
+
sf channel list --type group
|
|
379
|
+
sf channel list --member el-user123`,
|
|
380
|
+
options: channelListOptions,
|
|
381
|
+
handler: channelListHandler,
|
|
382
|
+
};
|
|
383
|
+
// ============================================================================
|
|
384
|
+
// Channel Members Command
|
|
385
|
+
// ============================================================================
|
|
386
|
+
async function channelMembersHandler(args, options) {
|
|
387
|
+
const [id] = args;
|
|
388
|
+
if (!id) {
|
|
389
|
+
return failure('Usage: sf channel members <id>\nExample: sf channel members el-abc123', ExitCode.INVALID_ARGUMENTS);
|
|
390
|
+
}
|
|
391
|
+
const { api, error } = createAPI(options);
|
|
392
|
+
if (error) {
|
|
393
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
const channel = await api.get(id);
|
|
397
|
+
if (!channel) {
|
|
398
|
+
return failure(`Channel not found: ${id}`, ExitCode.NOT_FOUND);
|
|
399
|
+
}
|
|
400
|
+
if (channel.type !== 'channel') {
|
|
401
|
+
return failure(`Element ${id} is not a channel (type: ${channel.type})`, ExitCode.VALIDATION);
|
|
402
|
+
}
|
|
403
|
+
const members = channel.members;
|
|
404
|
+
const modifiers = channel.permissions.modifyMembers;
|
|
405
|
+
const mode = getOutputMode(options);
|
|
406
|
+
const formatter = getFormatter(mode);
|
|
407
|
+
if (mode === 'json') {
|
|
408
|
+
return success({ members, modifiers, count: members.length });
|
|
409
|
+
}
|
|
410
|
+
if (mode === 'quiet') {
|
|
411
|
+
return success(members.join('\n'));
|
|
412
|
+
}
|
|
413
|
+
if (members.length === 0) {
|
|
414
|
+
return success({ members: [], count: 0 }, 'No members');
|
|
415
|
+
}
|
|
416
|
+
// Build table
|
|
417
|
+
const headers = ['MEMBER', 'ROLE'];
|
|
418
|
+
const rows = members.map((m) => [
|
|
419
|
+
m,
|
|
420
|
+
modifiers.includes(m) ? 'moderator' : 'member',
|
|
421
|
+
]);
|
|
422
|
+
const table = formatter.table(headers, rows);
|
|
423
|
+
return success({ members, modifiers, count: members.length }, table + `\n${members.length} member(s)`);
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
427
|
+
return failure(`Failed to list members: ${message}`, ExitCode.GENERAL_ERROR);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const channelMembersCommand = {
|
|
431
|
+
name: 'members',
|
|
432
|
+
description: 'List channel members',
|
|
433
|
+
usage: 'sf channel members <id>',
|
|
434
|
+
help: `List members of a channel.
|
|
435
|
+
|
|
436
|
+
Arguments:
|
|
437
|
+
id Channel identifier
|
|
438
|
+
|
|
439
|
+
Examples:
|
|
440
|
+
sf channel members el-abc123`,
|
|
441
|
+
handler: channelMembersHandler,
|
|
442
|
+
};
|
|
443
|
+
// ============================================================================
|
|
444
|
+
// Channel Add Command
|
|
445
|
+
// ============================================================================
|
|
446
|
+
async function channelAddHandler(args, options) {
|
|
447
|
+
const [id, entityId] = args;
|
|
448
|
+
if (!id || !entityId) {
|
|
449
|
+
return failure('Usage: sf channel add <channel-id> <entity-id>\nExample: sf channel add el-abc123 el-user456', ExitCode.INVALID_ARGUMENTS);
|
|
450
|
+
}
|
|
451
|
+
const { api, error } = createAPI(options);
|
|
452
|
+
if (error) {
|
|
453
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
const actor = resolveActor(options);
|
|
457
|
+
const result = await api.addChannelMember(id, entityId, { actor });
|
|
458
|
+
if (result.success) {
|
|
459
|
+
return success(result.channel, `Added ${entityId} to channel ${id}`);
|
|
460
|
+
}
|
|
461
|
+
return failure(`Failed to add member`, ExitCode.GENERAL_ERROR);
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
// Handle specific error cases with user-friendly messages
|
|
465
|
+
if (err instanceof Error) {
|
|
466
|
+
if (err.message.includes('not found')) {
|
|
467
|
+
return failure(`Channel not found: ${id}`, ExitCode.NOT_FOUND);
|
|
468
|
+
}
|
|
469
|
+
if (err.message.includes('not a channel')) {
|
|
470
|
+
return failure(`Element ${id} is not a channel`, ExitCode.VALIDATION);
|
|
471
|
+
}
|
|
472
|
+
if (err.message.includes('direct channel')) {
|
|
473
|
+
return failure('Cannot modify members of a direct channel', ExitCode.VALIDATION);
|
|
474
|
+
}
|
|
475
|
+
if (err.message.includes('Cannot modify members')) {
|
|
476
|
+
return failure('You do not have permission to add members to this channel', ExitCode.PERMISSION);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
480
|
+
return failure(`Failed to add member: ${message}`, ExitCode.GENERAL_ERROR);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const channelAddCommand = {
|
|
484
|
+
name: 'add',
|
|
485
|
+
description: 'Add a member to a channel',
|
|
486
|
+
usage: 'sf channel add <channel-id> <entity-id>',
|
|
487
|
+
help: `Add a member to a group channel.
|
|
488
|
+
|
|
489
|
+
Only group channels support adding members. Direct channels have fixed membership.
|
|
490
|
+
You must have permission to modify members (be in the modifyMembers list).
|
|
491
|
+
|
|
492
|
+
Arguments:
|
|
493
|
+
channel-id Channel identifier
|
|
494
|
+
entity-id Entity to add as member
|
|
495
|
+
|
|
496
|
+
Examples:
|
|
497
|
+
sf channel add el-abc123 el-user456`,
|
|
498
|
+
handler: channelAddHandler,
|
|
499
|
+
};
|
|
500
|
+
// ============================================================================
|
|
501
|
+
// Channel Remove Command
|
|
502
|
+
// ============================================================================
|
|
503
|
+
async function channelRemoveHandler(args, options) {
|
|
504
|
+
const [id, entityId] = args;
|
|
505
|
+
if (!id || !entityId) {
|
|
506
|
+
return failure('Usage: sf channel remove <channel-id> <entity-id>\nExample: sf channel remove el-abc123 el-user456', ExitCode.INVALID_ARGUMENTS);
|
|
507
|
+
}
|
|
508
|
+
const { api, error } = createAPI(options);
|
|
509
|
+
if (error) {
|
|
510
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
const actor = resolveActor(options);
|
|
514
|
+
const result = await api.removeChannelMember(id, entityId, { actor });
|
|
515
|
+
if (result.success) {
|
|
516
|
+
return success(result.channel, `Removed ${entityId} from channel ${id}`);
|
|
517
|
+
}
|
|
518
|
+
return failure(`Failed to remove member`, ExitCode.GENERAL_ERROR);
|
|
519
|
+
}
|
|
520
|
+
catch (err) {
|
|
521
|
+
// Handle specific error cases with user-friendly messages
|
|
522
|
+
if (err instanceof Error) {
|
|
523
|
+
if (err.message.includes('not found')) {
|
|
524
|
+
return failure(`Channel not found: ${id}`, ExitCode.NOT_FOUND);
|
|
525
|
+
}
|
|
526
|
+
if (err.message.includes('not a channel')) {
|
|
527
|
+
return failure(`Element ${id} is not a channel`, ExitCode.VALIDATION);
|
|
528
|
+
}
|
|
529
|
+
if (err.message.includes('direct channel')) {
|
|
530
|
+
return failure('Cannot modify members of a direct channel', ExitCode.VALIDATION);
|
|
531
|
+
}
|
|
532
|
+
if (err.message.includes('not a member')) {
|
|
533
|
+
return failure(`${entityId} is not a member of this channel`, ExitCode.VALIDATION);
|
|
534
|
+
}
|
|
535
|
+
if (err.message.includes('Cannot modify members')) {
|
|
536
|
+
return failure('You do not have permission to remove members from this channel', ExitCode.PERMISSION);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
540
|
+
return failure(`Failed to remove member: ${message}`, ExitCode.GENERAL_ERROR);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const channelRemoveCommand = {
|
|
544
|
+
name: 'remove',
|
|
545
|
+
description: 'Remove a member from a channel',
|
|
546
|
+
usage: 'sf channel remove <channel-id> <entity-id>',
|
|
547
|
+
help: `Remove a member from a group channel.
|
|
548
|
+
|
|
549
|
+
Only group channels support removing members. Direct channels have fixed membership.
|
|
550
|
+
You must have permission to modify members (be in the modifyMembers list).
|
|
551
|
+
|
|
552
|
+
Arguments:
|
|
553
|
+
channel-id Channel identifier
|
|
554
|
+
entity-id Entity to remove
|
|
555
|
+
|
|
556
|
+
Examples:
|
|
557
|
+
sf channel remove el-abc123 el-user456`,
|
|
558
|
+
handler: channelRemoveHandler,
|
|
559
|
+
};
|
|
560
|
+
// ============================================================================
|
|
561
|
+
// Channel Merge Command
|
|
562
|
+
// ============================================================================
|
|
563
|
+
async function channelMergeHandler(args, options) {
|
|
564
|
+
const sourceId = options.source;
|
|
565
|
+
const targetId = options.target;
|
|
566
|
+
const newName = options.name;
|
|
567
|
+
if (!sourceId || !targetId) {
|
|
568
|
+
return failure('Usage: sf channel merge --source <id> --target <id> [--name <new-name>]\nExample: sf channel merge --source el-abc123 --target el-def456', ExitCode.INVALID_ARGUMENTS);
|
|
569
|
+
}
|
|
570
|
+
const { api, error } = createAPI(options);
|
|
571
|
+
if (error) {
|
|
572
|
+
return failure(error, ExitCode.GENERAL_ERROR);
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
const actor = resolveActor(options);
|
|
576
|
+
const result = await api.mergeChannels(sourceId, targetId, { newName, actor });
|
|
577
|
+
return success(result, `Merged channel ${sourceId} into ${targetId} (${result.messagesMoved} messages moved)`);
|
|
578
|
+
}
|
|
579
|
+
catch (err) {
|
|
580
|
+
if (err instanceof Error) {
|
|
581
|
+
if (err.message.includes('not found')) {
|
|
582
|
+
return failure(`Channel not found`, ExitCode.NOT_FOUND);
|
|
583
|
+
}
|
|
584
|
+
if (err.message.includes('not a group')) {
|
|
585
|
+
return failure('Only group channels can be merged', ExitCode.VALIDATION);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
589
|
+
return failure(`Failed to merge channels: ${message}`, ExitCode.GENERAL_ERROR);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const channelMergeOptions = [
|
|
593
|
+
{ name: 'source', short: 's', hasValue: true, description: 'Source channel ID (will be archived)', required: true },
|
|
594
|
+
{ name: 'target', short: 't', hasValue: true, description: 'Target channel ID (will receive messages)', required: true },
|
|
595
|
+
{ name: 'name', short: 'n', hasValue: true, description: 'Optional new name for the target channel' },
|
|
596
|
+
];
|
|
597
|
+
const channelMergeCommand = {
|
|
598
|
+
name: 'merge',
|
|
599
|
+
description: 'Merge two group channels',
|
|
600
|
+
usage: 'sf channel merge --source <id> --target <id> [--name <new-name>]',
|
|
601
|
+
options: channelMergeOptions,
|
|
602
|
+
help: `Merge a source channel into a target channel.
|
|
603
|
+
|
|
604
|
+
All messages from the source channel are moved to the target channel.
|
|
605
|
+
Members from both channels are combined. The source channel is archived.
|
|
606
|
+
|
|
607
|
+
Only group channels can be merged. Direct channels are not supported.
|
|
608
|
+
|
|
609
|
+
Options:
|
|
610
|
+
--source, -s Source channel ID (will be archived after merge)
|
|
611
|
+
--target, -t Target channel ID (will receive all messages and members)
|
|
612
|
+
--name, -n Optional new name for the merged target channel
|
|
613
|
+
|
|
614
|
+
Examples:
|
|
615
|
+
sf channel merge --source el-abc123 --target el-def456
|
|
616
|
+
sf channel merge -s el-abc123 -t el-def456 --name combined-channel`,
|
|
617
|
+
handler: channelMergeHandler,
|
|
618
|
+
};
|
|
619
|
+
// ============================================================================
|
|
620
|
+
// Channel Root Command
|
|
621
|
+
// ============================================================================
|
|
622
|
+
export const channelCommand = {
|
|
623
|
+
name: 'channel',
|
|
624
|
+
description: 'Manage channels (message containers)',
|
|
625
|
+
usage: 'sf channel <subcommand> [options]',
|
|
626
|
+
help: `Manage channels - containers for messages between entities.
|
|
627
|
+
|
|
628
|
+
Channels support both direct messaging (1:1) and group conversations.
|
|
629
|
+
Group channels have configurable visibility and join policies.
|
|
630
|
+
|
|
631
|
+
Subcommands:
|
|
632
|
+
create Create a new channel
|
|
633
|
+
join Join a channel
|
|
634
|
+
leave Leave a channel
|
|
635
|
+
list List channels
|
|
636
|
+
members List channel members
|
|
637
|
+
add Add a member to a channel
|
|
638
|
+
remove Remove a member from a channel
|
|
639
|
+
merge Merge two group channels
|
|
640
|
+
|
|
641
|
+
Examples:
|
|
642
|
+
sf channel create --name general
|
|
643
|
+
sf channel list --member el-user123
|
|
644
|
+
sf channel join el-abc123
|
|
645
|
+
sf channel members el-abc123
|
|
646
|
+
sf channel add el-abc123 el-user456
|
|
647
|
+
sf channel remove el-abc123 el-user456
|
|
648
|
+
sf channel merge --source el-abc123 --target el-def456
|
|
649
|
+
|
|
650
|
+
Note: Use 'sf show <id>', 'sf update <id>', 'sf delete <id>' for any element.`,
|
|
651
|
+
subcommands: {
|
|
652
|
+
create: channelCreateCommand,
|
|
653
|
+
join: channelJoinCommand,
|
|
654
|
+
leave: channelLeaveCommand,
|
|
655
|
+
list: channelListCommand,
|
|
656
|
+
members: channelMembersCommand,
|
|
657
|
+
add: channelAddCommand,
|
|
658
|
+
remove: channelRemoveCommand,
|
|
659
|
+
merge: channelMergeCommand,
|
|
660
|
+
// Aliases (hidden from --help via dedup in getCommandHelp)
|
|
661
|
+
new: channelCreateCommand,
|
|
662
|
+
ls: channelListCommand,
|
|
663
|
+
},
|
|
664
|
+
handler: async (args, options) => {
|
|
665
|
+
// Default to list if no subcommand
|
|
666
|
+
if (args.length === 0) {
|
|
667
|
+
return channelListHandler(args, options);
|
|
668
|
+
}
|
|
669
|
+
// Show "did you mean?" for unknown subcommands
|
|
670
|
+
const subNames = Object.keys(channelCommand.subcommands);
|
|
671
|
+
const suggestions = suggestCommands(args[0], subNames);
|
|
672
|
+
let msg = `Unknown subcommand: ${args[0]}`;
|
|
673
|
+
if (suggestions.length > 0) {
|
|
674
|
+
msg += `\n\nDid you mean?\n${suggestions.map(s => ` ${s}`).join('\n')}`;
|
|
675
|
+
}
|
|
676
|
+
msg += '\n\nRun "sf channel --help" to see available subcommands.';
|
|
677
|
+
return failure(msg, ExitCode.INVALID_ARGUMENTS);
|
|
678
|
+
},
|
|
679
|
+
};
|
|
680
|
+
//# sourceMappingURL=channel.js.map
|