twentythree-cli 1.0.1 → 1.1.1
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/commands/app/list.cjs +87 -0
- package/dist/commands/app/remove-thumbnail.cjs +51 -0
- package/dist/commands/app/set-thumbnail.cjs +78 -0
- package/dist/commands/auth/credentials.cjs +2 -1
- package/dist/commands/autocomplete/index.cjs +79 -0
- package/dist/commands/video/list.cjs +2 -1
- package/dist/commands/video/replace.cjs +2 -1
- package/dist/commands/video/section/generate.cjs +54 -0
- package/dist/commands/video/update.cjs +7 -4
- package/dist/commands/webinar/mail/preview.cjs +16 -3
- package/dist/lib/base-command.cjs +30 -2
- package/dist/lib/detect-shell.cjs +13 -0
- package/docs/guides/getting-started.md +17 -1
- package/oclif.manifest.json +5177 -4854
- package/package.json +10 -5
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_lib_term_map = require("../../lib/term-map.cjs");
|
|
3
|
+
const require_lib_base_command = require("../../lib/base-command.cjs");
|
|
4
|
+
const require_lib_output = require("../../lib/output.cjs");
|
|
5
|
+
let _oclif_core = require("@oclif/core");
|
|
6
|
+
//#region src/commands/app/list.ts
|
|
7
|
+
/**
|
|
8
|
+
* App list command — returns paginated list of player design apps on the workspace.
|
|
9
|
+
*/
|
|
10
|
+
var AppList = class AppList extends require_lib_base_command.AuthenticatedCommand {
|
|
11
|
+
static description = "List player design apps on the active workspace";
|
|
12
|
+
static examples = [
|
|
13
|
+
"<%= config.bin %> app list",
|
|
14
|
+
"<%= config.bin %> app list --app-id 42",
|
|
15
|
+
"<%= config.bin %> app list --page 2 --size 50",
|
|
16
|
+
"<%= config.bin %> app list --json"
|
|
17
|
+
];
|
|
18
|
+
static enableJsonFlag = true;
|
|
19
|
+
static agentMetadata = {
|
|
20
|
+
api_endpoint: "POST /app/list",
|
|
21
|
+
auth_scope: "read",
|
|
22
|
+
output_shape: {
|
|
23
|
+
type: "table",
|
|
24
|
+
columns: [
|
|
25
|
+
"ID",
|
|
26
|
+
"Name",
|
|
27
|
+
"Type",
|
|
28
|
+
"Description"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
side_effects: "none"
|
|
32
|
+
};
|
|
33
|
+
static flags = {
|
|
34
|
+
...require_lib_base_command.AuthenticatedCommand.baseFlags,
|
|
35
|
+
"app-id": _oclif_core.Flags.integer({
|
|
36
|
+
description: "Filter results to a specific app ID",
|
|
37
|
+
required: false
|
|
38
|
+
}),
|
|
39
|
+
page: _oclif_core.Flags.integer({
|
|
40
|
+
description: "Page offset",
|
|
41
|
+
required: false
|
|
42
|
+
}),
|
|
43
|
+
size: _oclif_core.Flags.integer({
|
|
44
|
+
description: "Number of results per page (default 20, max 100)",
|
|
45
|
+
required: false
|
|
46
|
+
})
|
|
47
|
+
};
|
|
48
|
+
static args = {};
|
|
49
|
+
async run() {
|
|
50
|
+
const { flags } = await this.parse(AppList);
|
|
51
|
+
this.printWorkspaceHeader();
|
|
52
|
+
const body = {};
|
|
53
|
+
if (flags["app-id"] !== void 0) body.app_id = flags["app-id"];
|
|
54
|
+
if (flags.page !== void 0) body.p = flags.page;
|
|
55
|
+
if (flags.size !== void 0) body.size = flags.size;
|
|
56
|
+
const { data, error } = await this.apiClient.POST("/app/list", {
|
|
57
|
+
body,
|
|
58
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
59
|
+
});
|
|
60
|
+
if (error) this.error(require_lib_term_map.applyCliTerms(require_lib_output.formatApiError(error)), { exit: 1 });
|
|
61
|
+
const apps = data?.apps ?? data?.data ?? [];
|
|
62
|
+
if (this.jsonEnabled()) return require_lib_output.formatJsonOutput({
|
|
63
|
+
ok: true,
|
|
64
|
+
data: apps,
|
|
65
|
+
summary: "App list",
|
|
66
|
+
breadcrumbs: [{ domain: this.activeWorkspace.domain }, { resource: "app" }]
|
|
67
|
+
});
|
|
68
|
+
if (apps.length === 0) {
|
|
69
|
+
this.log("No apps found.");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const table = require_lib_output.renderTable([
|
|
73
|
+
"ID",
|
|
74
|
+
"Name",
|
|
75
|
+
"Type",
|
|
76
|
+
"Description"
|
|
77
|
+
], apps.map((a) => [
|
|
78
|
+
String(a.app_id ?? a.id ?? ""),
|
|
79
|
+
String(a.name ?? ""),
|
|
80
|
+
String(a.type ?? ""),
|
|
81
|
+
String(a.description ?? "")
|
|
82
|
+
]));
|
|
83
|
+
this.log(table.toString());
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
module.exports = AppList;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_lib_term_map = require("../../lib/term-map.cjs");
|
|
3
|
+
const require_lib_base_command = require("../../lib/base-command.cjs");
|
|
4
|
+
const require_lib_output = require("../../lib/output.cjs");
|
|
5
|
+
let _oclif_core = require("@oclif/core");
|
|
6
|
+
let chalk = require("chalk");
|
|
7
|
+
chalk = require_runtime.__toESM(chalk);
|
|
8
|
+
//#region src/commands/app/remove-thumbnail.ts
|
|
9
|
+
/**
|
|
10
|
+
* App remove-thumbnail command — removes the custom thumbnail for an app, reverting to default.
|
|
11
|
+
*
|
|
12
|
+
* Threat mitigations:
|
|
13
|
+
* T-08-05: extends AuthenticatedCommand — anonymous mode rejected
|
|
14
|
+
*/
|
|
15
|
+
var AppRemoveThumbnail = class AppRemoveThumbnail extends require_lib_base_command.AuthenticatedCommand {
|
|
16
|
+
static description = "Remove the custom thumbnail for an app, reverting to the default";
|
|
17
|
+
static examples = ["<%= config.bin %> app remove-thumbnail 42", "<%= config.bin %> app remove-thumbnail 42 --json"];
|
|
18
|
+
static enableJsonFlag = true;
|
|
19
|
+
static agentMetadata = {
|
|
20
|
+
api_endpoint: "POST /app/remove-thumbnail",
|
|
21
|
+
auth_scope: "write",
|
|
22
|
+
output_shape: { type: "key-value" },
|
|
23
|
+
side_effects: "updates"
|
|
24
|
+
};
|
|
25
|
+
static flags = { ...require_lib_base_command.AuthenticatedCommand.baseFlags };
|
|
26
|
+
static args = { id: _oclif_core.Args.string({
|
|
27
|
+
description: "App ID",
|
|
28
|
+
required: true
|
|
29
|
+
}) };
|
|
30
|
+
async run() {
|
|
31
|
+
const { args } = await this.parse(AppRemoveThumbnail);
|
|
32
|
+
this.printWorkspaceHeader();
|
|
33
|
+
const { data, error } = await this.apiClient.POST("/app/remove-thumbnail", {
|
|
34
|
+
body: { app_id: Number(args.id) },
|
|
35
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
36
|
+
});
|
|
37
|
+
if (error) this.error(require_lib_term_map.applyCliTerms(require_lib_output.formatApiError(error)), { exit: 1 });
|
|
38
|
+
this.log(chalk.default.green(`Thumbnail removed for app ${args.id}`));
|
|
39
|
+
if (this.jsonEnabled()) return require_lib_output.formatJsonOutput({
|
|
40
|
+
ok: true,
|
|
41
|
+
data,
|
|
42
|
+
summary: `Thumbnail removed for app ${args.id}`,
|
|
43
|
+
breadcrumbs: [{ domain: this.activeWorkspace.domain }, {
|
|
44
|
+
resource: "app",
|
|
45
|
+
id: args.id
|
|
46
|
+
}]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
module.exports = AppRemoveThumbnail;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_lib_term_map = require("../../lib/term-map.cjs");
|
|
3
|
+
const require_lib_base_command = require("../../lib/base-command.cjs");
|
|
4
|
+
const require_lib_output = require("../../lib/output.cjs");
|
|
5
|
+
let _oclif_core = require("@oclif/core");
|
|
6
|
+
let chalk = require("chalk");
|
|
7
|
+
chalk = require_runtime.__toESM(chalk);
|
|
8
|
+
let node_fs = require("node:fs");
|
|
9
|
+
node_fs = require_runtime.__toESM(node_fs);
|
|
10
|
+
let node_path = require("node:path");
|
|
11
|
+
node_path = require_runtime.__toESM(node_path);
|
|
12
|
+
//#region src/commands/app/set-thumbnail.ts
|
|
13
|
+
/**
|
|
14
|
+
* App set-thumbnail command — uploads and sets a custom thumbnail image for an app.
|
|
15
|
+
*
|
|
16
|
+
* Uses direct multipart POST with bodySerializer to create FormData.
|
|
17
|
+
* Per D-3: NOT the chunked engine — thumbnail images use direct multipart upload.
|
|
18
|
+
*
|
|
19
|
+
* Threat mitigations:
|
|
20
|
+
* T-08-08: Validates file exists via fs.existsSync before reading
|
|
21
|
+
* T-08-05: extends AuthenticatedCommand — anonymous mode rejected
|
|
22
|
+
*/
|
|
23
|
+
var AppSetThumbnail = class AppSetThumbnail extends require_lib_base_command.AuthenticatedCommand {
|
|
24
|
+
static description = "Upload and set a custom thumbnail image for an app";
|
|
25
|
+
static examples = ["<%= config.bin %> app set-thumbnail ./thumbnail.png --app-id 42", "<%= config.bin %> app set-thumbnail ./thumbnail.jpg --app-id 42 --json"];
|
|
26
|
+
static enableJsonFlag = true;
|
|
27
|
+
static agentMetadata = {
|
|
28
|
+
api_endpoint: "POST /app/set-thumbnail",
|
|
29
|
+
auth_scope: "write",
|
|
30
|
+
output_shape: { type: "key-value" },
|
|
31
|
+
side_effects: "updates"
|
|
32
|
+
};
|
|
33
|
+
static flags = {
|
|
34
|
+
...require_lib_base_command.AuthenticatedCommand.baseFlags,
|
|
35
|
+
"app-id": _oclif_core.Flags.integer({
|
|
36
|
+
description: "App ID to update",
|
|
37
|
+
required: true
|
|
38
|
+
})
|
|
39
|
+
};
|
|
40
|
+
static args = { file: _oclif_core.Args.string({
|
|
41
|
+
description: "Path to the thumbnail image file",
|
|
42
|
+
required: true
|
|
43
|
+
}) };
|
|
44
|
+
async run() {
|
|
45
|
+
const { args, flags } = await this.parse(AppSetThumbnail);
|
|
46
|
+
this.printWorkspaceHeader();
|
|
47
|
+
const filePath = node_path.resolve(args.file);
|
|
48
|
+
if (!node_fs.existsSync(filePath)) this.error(`File not found: ${filePath}`, { exit: 1 });
|
|
49
|
+
const fileBuffer = node_fs.readFileSync(filePath);
|
|
50
|
+
const fileName = node_path.basename(filePath);
|
|
51
|
+
const fileBlob = new Blob([fileBuffer]);
|
|
52
|
+
const { data, error } = await this.apiClient.POST("/app/set-thumbnail", {
|
|
53
|
+
body: {
|
|
54
|
+
app_id: flags["app-id"],
|
|
55
|
+
file: fileBlob
|
|
56
|
+
},
|
|
57
|
+
bodySerializer(body) {
|
|
58
|
+
const fd = new FormData();
|
|
59
|
+
for (const [k, v] of Object.entries(body)) if (v !== void 0) if (v instanceof Blob) fd.append(k, v, fileName);
|
|
60
|
+
else fd.append(k, String(v));
|
|
61
|
+
return fd;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (error) this.error(require_lib_term_map.applyCliTerms(require_lib_output.formatApiError(error)), { exit: 1 });
|
|
65
|
+
this.log(chalk.default.green(`Thumbnail set for app ${flags["app-id"]}`));
|
|
66
|
+
if (this.jsonEnabled()) return require_lib_output.formatJsonOutput({
|
|
67
|
+
ok: true,
|
|
68
|
+
data,
|
|
69
|
+
summary: `Thumbnail set for app ${flags["app-id"]}`,
|
|
70
|
+
breadcrumbs: [{ domain: this.activeWorkspace.domain }, {
|
|
71
|
+
resource: "app",
|
|
72
|
+
id: String(flags["app-id"])
|
|
73
|
+
}]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
module.exports = AppSetThumbnail;
|
|
@@ -40,13 +40,14 @@ var Credentials = class Credentials extends _oclif_core.Command {
|
|
|
40
40
|
require_auth_workspace_config.setCredentialDomain(domain);
|
|
41
41
|
const s = _clack_prompts.spinner();
|
|
42
42
|
s.start("Discovering workspaces...");
|
|
43
|
-
let workspaces;
|
|
43
|
+
let workspaces = [];
|
|
44
44
|
try {
|
|
45
45
|
workspaces = await require_auth_token_refresh.fetchWorkspaceTokens(domain, trimmedToken);
|
|
46
46
|
s.stop("Workspaces discovered");
|
|
47
47
|
} catch (err) {
|
|
48
48
|
s.stop("Failed to discover workspaces");
|
|
49
49
|
this.error(`Could not discover workspaces: ${err instanceof Error ? err.message : String(err)}`, { exit: 1 });
|
|
50
|
+
return;
|
|
50
51
|
}
|
|
51
52
|
let selectedWorkspaces;
|
|
52
53
|
if (workspaces.length > 1) {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_lib_detect_shell = require("../../lib/detect-shell.cjs");
|
|
3
|
+
let _oclif_core = require("@oclif/core");
|
|
4
|
+
let _clack_prompts = require("@clack/prompts");
|
|
5
|
+
_clack_prompts = require_runtime.__toESM(_clack_prompts);
|
|
6
|
+
//#region src/commands/autocomplete/index.ts
|
|
7
|
+
var Autocomplete = class Autocomplete extends _oclif_core.Command {
|
|
8
|
+
static description = "Set up tab completion for your shell";
|
|
9
|
+
static agentMetadata = {
|
|
10
|
+
api_endpoint: "interactive",
|
|
11
|
+
auth_scope: "none",
|
|
12
|
+
output_shape: { type: "none" },
|
|
13
|
+
side_effects: "creates"
|
|
14
|
+
};
|
|
15
|
+
static examples = ["<%= config.bin %> autocomplete"];
|
|
16
|
+
async run() {
|
|
17
|
+
await this.parse(Autocomplete);
|
|
18
|
+
_clack_prompts.intro("Tab completion setup");
|
|
19
|
+
const detectedShell = require_lib_detect_shell.detectShell(process.env.SHELL ?? "");
|
|
20
|
+
let shell;
|
|
21
|
+
if (detectedShell) {
|
|
22
|
+
const confirm = await _clack_prompts.confirm({ message: `Detected shell: ${detectedShell}. Set up completion for ${detectedShell}?` });
|
|
23
|
+
if (_clack_prompts.isCancel(confirm)) {
|
|
24
|
+
_clack_prompts.cancel("Cancelled");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (confirm) shell = detectedShell;
|
|
28
|
+
else {
|
|
29
|
+
const chosen = await _clack_prompts.select({
|
|
30
|
+
message: "Select your shell",
|
|
31
|
+
options: [{
|
|
32
|
+
value: "zsh",
|
|
33
|
+
label: "zsh"
|
|
34
|
+
}, {
|
|
35
|
+
value: "bash",
|
|
36
|
+
label: "bash"
|
|
37
|
+
}]
|
|
38
|
+
});
|
|
39
|
+
if (_clack_prompts.isCancel(chosen)) {
|
|
40
|
+
_clack_prompts.cancel("Cancelled");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
shell = chosen;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
const chosen = await _clack_prompts.select({
|
|
47
|
+
message: "Select your shell",
|
|
48
|
+
options: [{
|
|
49
|
+
value: "zsh",
|
|
50
|
+
label: "zsh"
|
|
51
|
+
}, {
|
|
52
|
+
value: "bash",
|
|
53
|
+
label: "bash"
|
|
54
|
+
}]
|
|
55
|
+
});
|
|
56
|
+
if (_clack_prompts.isCancel(chosen)) {
|
|
57
|
+
_clack_prompts.cancel("Cancelled");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
shell = chosen;
|
|
61
|
+
}
|
|
62
|
+
const s = _clack_prompts.spinner();
|
|
63
|
+
s.start("Building completion cache...");
|
|
64
|
+
try {
|
|
65
|
+
await this.config.runCommand("autocomplete:create", []);
|
|
66
|
+
s.stop("Completion cache built");
|
|
67
|
+
} catch (err) {
|
|
68
|
+
s.stop("Failed to build completion cache");
|
|
69
|
+
this.error(`Could not build completion cache: ${err instanceof Error ? err.message : String(err)}`, { exit: 1 });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const rcFile = shell === "zsh" ? "~/.zshrc" : "~/.bashrc";
|
|
73
|
+
const evalLine = `grep -qF 'twentythree autocomplete script ${shell}' ${rcFile} || printf "$(twentythree autocomplete script ${shell})" >> ${rcFile}; source ${rcFile}`;
|
|
74
|
+
_clack_prompts.note(`Add tab completion to your shell by running:\n\n ${evalLine}\n\nThen restart your terminal or run: source ${rcFile}`, "Setup instructions");
|
|
75
|
+
_clack_prompts.outro("After setup, try: twentythree video <TAB>");
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
module.exports = Autocomplete;
|
|
@@ -52,7 +52,7 @@ var VideoList = class VideoList extends require_lib_base_command.AuthenticatedCo
|
|
|
52
52
|
async run() {
|
|
53
53
|
const { flags } = await this.parse(VideoList);
|
|
54
54
|
this.printWorkspaceHeader();
|
|
55
|
-
const
|
|
55
|
+
const allVideos = await require_lib_pagination.fetchAllPages(async (page, size) => {
|
|
56
56
|
const { data, error } = await this.apiClient.GET("/photo/list", { params: { query: {
|
|
57
57
|
p: page,
|
|
58
58
|
size,
|
|
@@ -65,6 +65,7 @@ var VideoList = class VideoList extends require_lib_base_command.AuthenticatedCo
|
|
|
65
65
|
total_count: resp?.total_count
|
|
66
66
|
};
|
|
67
67
|
});
|
|
68
|
+
const videos = flags.limit !== void 0 ? allVideos.slice(0, flags.limit) : allVideos;
|
|
68
69
|
if (this.jsonEnabled()) return require_lib_output.formatJsonOutput({
|
|
69
70
|
ok: true,
|
|
70
71
|
data: videos,
|
|
@@ -81,7 +81,7 @@ var VideoReplace = class VideoReplace extends require_lib_base_command.Authentic
|
|
|
81
81
|
this.error(`File not found: ${args.file}`, { exit: 1 });
|
|
82
82
|
}
|
|
83
83
|
const { data: tokenData, error: tokenError } = await this.apiClient.GET("/photo/get-replace-token", { params: { query: { photo_id: Number(args.id) } } });
|
|
84
|
-
if (tokenError) this.error(require_lib_term_map.applyCliTerms(
|
|
84
|
+
if (tokenError) this.error(require_lib_term_map.applyCliTerms(require_lib_output.formatApiError(tokenError)), { exit: 1 });
|
|
85
85
|
const replaceToken = tokenData?.data?.replace_token;
|
|
86
86
|
if (!replaceToken) this.error("Failed to obtain replace token from API", { exit: 1 });
|
|
87
87
|
const totalBytes = (await (0, node_fs_promises.stat)(args.file)).size;
|
|
@@ -107,6 +107,7 @@ var VideoReplace = class VideoReplace extends require_lib_base_command.Authentic
|
|
|
107
107
|
} finally {
|
|
108
108
|
bar.finish();
|
|
109
109
|
}
|
|
110
|
+
if (!result) return;
|
|
110
111
|
const adminUrl = `https://${this.activeWorkspace.domain}/manage/video/${args.id}`;
|
|
111
112
|
this.log(chalk.default.green(`Video ${args.id} replaced successfully`));
|
|
112
113
|
this.log(`ID: ${args.id}`);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_lib_term_map = require("../../../lib/term-map.cjs");
|
|
3
|
+
const require_lib_base_command = require("../../../lib/base-command.cjs");
|
|
4
|
+
const require_lib_output = require("../../../lib/output.cjs");
|
|
5
|
+
let _oclif_core = require("@oclif/core");
|
|
6
|
+
let chalk = require("chalk");
|
|
7
|
+
chalk = require_runtime.__toESM(chalk);
|
|
8
|
+
//#region src/commands/video/section/generate.ts
|
|
9
|
+
/**
|
|
10
|
+
* Video section generate command — AI-generates sections from a video transcript.
|
|
11
|
+
*
|
|
12
|
+
* Replaces all existing sections. Requires a transcript to be available.
|
|
13
|
+
*/
|
|
14
|
+
var VideoSectionGenerate = class VideoSectionGenerate extends require_lib_base_command.AuthenticatedCommand {
|
|
15
|
+
static description = "Automatically generate sections for a video using AI (requires transcript)";
|
|
16
|
+
static examples = ["<%= config.bin %> video section generate 12345", "<%= config.bin %> video section generate 12345 --json"];
|
|
17
|
+
static enableJsonFlag = true;
|
|
18
|
+
static agentMetadata = {
|
|
19
|
+
api_endpoint: "POST /photo/section/generate",
|
|
20
|
+
auth_scope: "write",
|
|
21
|
+
output_shape: { type: "key-value" },
|
|
22
|
+
side_effects: "creates"
|
|
23
|
+
};
|
|
24
|
+
static flags = { ...require_lib_base_command.AuthenticatedCommand.baseFlags };
|
|
25
|
+
static args = { id: _oclif_core.Args.string({
|
|
26
|
+
description: "Video ID",
|
|
27
|
+
required: true
|
|
28
|
+
}) };
|
|
29
|
+
async run() {
|
|
30
|
+
const { args } = await this.parse(VideoSectionGenerate);
|
|
31
|
+
this.printWorkspaceHeader();
|
|
32
|
+
const { data, error } = await this.apiClient.POST("/photo/section/generate", {
|
|
33
|
+
body: { photo_id: Number(args.id) },
|
|
34
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
35
|
+
});
|
|
36
|
+
if (error) this.error(require_lib_term_map.applyCliTerms(require_lib_output.formatApiError(error)), { exit: 1 });
|
|
37
|
+
this.log(chalk.default.green(`Sections generated for video ${args.id}`));
|
|
38
|
+
if (this.jsonEnabled()) return require_lib_output.formatJsonOutput({
|
|
39
|
+
ok: true,
|
|
40
|
+
data,
|
|
41
|
+
summary: `Sections generated for video ${args.id}`,
|
|
42
|
+
breadcrumbs: [
|
|
43
|
+
{ domain: this.activeWorkspace.domain },
|
|
44
|
+
{
|
|
45
|
+
resource: "video",
|
|
46
|
+
id: args.id
|
|
47
|
+
},
|
|
48
|
+
{ resource: "section" }
|
|
49
|
+
]
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
module.exports = VideoSectionGenerate;
|
|
@@ -146,9 +146,12 @@ var VideoUpdate = class VideoUpdate extends require_lib_base_command.Authenticat
|
|
|
146
146
|
initialValue: current.published_p ? "yes" : "no"
|
|
147
147
|
});
|
|
148
148
|
if ((0, _clack_prompts.isCancel)(publishedResult)) process.exit(2);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
const titleVal = titleResult;
|
|
150
|
+
const descriptionVal = descriptionResult;
|
|
151
|
+
const tagsVal = tagsResult;
|
|
152
|
+
if (titleVal !== "") body.title = titleVal;
|
|
153
|
+
if (descriptionVal !== "" || current.content_text === "") body.description = descriptionVal;
|
|
154
|
+
if (tagsVal !== "" || currentTags === "") body.tags = tagsVal;
|
|
152
155
|
body.published_p = publishedResult === "yes" ? 1 : 0;
|
|
153
156
|
} else {
|
|
154
157
|
if (flags.title !== void 0) body.title = flags.title;
|
|
@@ -167,7 +170,7 @@ var VideoUpdate = class VideoUpdate extends require_lib_base_command.Authenticat
|
|
|
167
170
|
body,
|
|
168
171
|
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
169
172
|
});
|
|
170
|
-
if (updateError) this.error(require_lib_term_map.applyCliTerms(
|
|
173
|
+
if (updateError) this.error(require_lib_term_map.applyCliTerms(require_lib_output.formatApiError(updateError)), { exit: 1 });
|
|
171
174
|
this.log(chalk.default.green(`Video ${args.id} updated`));
|
|
172
175
|
if (this.jsonEnabled()) return require_lib_output.formatJsonOutput({
|
|
173
176
|
ok: true,
|
|
@@ -49,7 +49,10 @@ var WebinarMailPreview = class WebinarMailPreview extends require_lib_base_comma
|
|
|
49
49
|
const { args, flags } = await this.parse(WebinarMailPreview);
|
|
50
50
|
this.printWorkspaceHeader();
|
|
51
51
|
const contextField = flags["webinar-id"] ? { live_id: Number(flags["webinar-id"]) } : flags["series-id"] ? { live_series_id: Number(flags["series-id"]) } : null;
|
|
52
|
-
if (!contextField)
|
|
52
|
+
if (!contextField) {
|
|
53
|
+
this.error(require_lib_term_map.applyCliTerms("Either --webinar-id or --series-id is required"), { exit: 1 });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
53
56
|
const query = new URLSearchParams({
|
|
54
57
|
...Object.fromEntries(Object.entries(contextField).map(([k, v]) => [k, String(v)])),
|
|
55
58
|
live_mail_id: String(args.id)
|
|
@@ -57,8 +60,18 @@ var WebinarMailPreview = class WebinarMailPreview extends require_lib_base_comma
|
|
|
57
60
|
const headers = {};
|
|
58
61
|
if (this.activeWorkspace.bearer_token) headers["Authorization"] = `Bearer ${this.activeWorkspace.bearer_token}`;
|
|
59
62
|
const response = await fetch(`${this.apiBaseUrl}live/mail/preview?${query}`, { headers });
|
|
60
|
-
const
|
|
61
|
-
if (!response.ok)
|
|
63
|
+
const body = await response.text();
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
let errMsg;
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(body);
|
|
68
|
+
errMsg = parsed?.message ?? parsed?.error ?? `status ${response.status}`;
|
|
69
|
+
} catch {
|
|
70
|
+
errMsg = body.slice(0, 200) || `status ${response.status}`;
|
|
71
|
+
}
|
|
72
|
+
this.error(require_lib_term_map.applyCliTerms(`API error: ${errMsg}`), { exit: 1 });
|
|
73
|
+
}
|
|
74
|
+
const html = body;
|
|
62
75
|
if (this.jsonEnabled()) return require_lib_output.formatJsonOutput({
|
|
63
76
|
ok: true,
|
|
64
77
|
data: html,
|
|
@@ -7,6 +7,7 @@ let _oclif_core = require("@oclif/core");
|
|
|
7
7
|
let chalk = require("chalk");
|
|
8
8
|
chalk = require_runtime.__toESM(chalk);
|
|
9
9
|
let _clack_prompts = require("@clack/prompts");
|
|
10
|
+
_clack_prompts = require_runtime.__toESM(_clack_prompts);
|
|
10
11
|
//#region src/lib/base-command.ts
|
|
11
12
|
var BaseCommand = class extends _oclif_core.Command {
|
|
12
13
|
static enableJsonFlag = true;
|
|
@@ -71,7 +72,7 @@ var BaseCommand = class extends _oclif_core.Command {
|
|
|
71
72
|
const result = require_auth_workspace_config.findWorkspace(workspaceFlagValue, require_auth_workspace_config.getWorkspaces());
|
|
72
73
|
if (result === null) this.error(`No workspace matching '${workspaceFlagValue}' found — run \`twentythree workspace list\` to see available workspaces`, { exit: 1 });
|
|
73
74
|
else if (Array.isArray(result)) {
|
|
74
|
-
const chosen = await
|
|
75
|
+
const chosen = await _clack_prompts.select({
|
|
75
76
|
message: `Multiple workspaces match '${workspaceFlagValue}'. Select one:`,
|
|
76
77
|
options: result.map((w) => ({
|
|
77
78
|
value: w.domain,
|
|
@@ -79,7 +80,9 @@ var BaseCommand = class extends _oclif_core.Command {
|
|
|
79
80
|
}))
|
|
80
81
|
});
|
|
81
82
|
if (typeof chosen === "symbol") this.error("Workspace selection cancelled", { exit: 1 });
|
|
82
|
-
|
|
83
|
+
const found = require_auth_workspace_config.getWorkspaceForDomain(chosen);
|
|
84
|
+
if (!found) this.error(`Workspace '${chosen}' could not be resolved — try running \`twentythree workspace list\``, { exit: 1 });
|
|
85
|
+
resolved = found;
|
|
83
86
|
} else resolved = result;
|
|
84
87
|
} else {
|
|
85
88
|
const activeDomain = require_auth_workspace_config.getActiveWorkspace();
|
|
@@ -100,6 +103,31 @@ var BaseCommand = class extends _oclif_core.Command {
|
|
|
100
103
|
token: this.activeWorkspace.bearer_token || void 0
|
|
101
104
|
});
|
|
102
105
|
}
|
|
106
|
+
async catch(err) {
|
|
107
|
+
if (!process.stdin.isTTY) return super.catch(err);
|
|
108
|
+
if (err.constructor.name !== "FailedFlagValidationError") return super.catch(err);
|
|
109
|
+
const flagNames = [...err.message.matchAll(/Missing required flag ([^\n]+)/g)].map((m) => m[1]);
|
|
110
|
+
if (flagNames.length === 0) return super.catch(err);
|
|
111
|
+
const inputFlags = err.parse?.input?.flags ?? {};
|
|
112
|
+
_clack_prompts.intro("Missing required input");
|
|
113
|
+
const extraArgv = [];
|
|
114
|
+
for (const flagName of flagNames) {
|
|
115
|
+
const flagDef = inputFlags[flagName];
|
|
116
|
+
const label = flagDef?.description ?? flagDef?.summary ?? flagName;
|
|
117
|
+
const value = await _clack_prompts.text({
|
|
118
|
+
message: label,
|
|
119
|
+
validate: (v) => !v || v.trim().length === 0 ? "Value is required" : void 0
|
|
120
|
+
});
|
|
121
|
+
if (_clack_prompts.isCancel(value)) {
|
|
122
|
+
_clack_prompts.cancel("Cancelled");
|
|
123
|
+
throw new _oclif_core.Errors.CLIError("Cancelled", { exit: 0 });
|
|
124
|
+
}
|
|
125
|
+
extraArgv.push(`--${flagName}`, value);
|
|
126
|
+
}
|
|
127
|
+
_clack_prompts.outro("Running command...");
|
|
128
|
+
const newArgv = [...this.argv ?? [], ...extraArgv];
|
|
129
|
+
await this.config.runCommand(this.id, newArgv);
|
|
130
|
+
}
|
|
103
131
|
/**
|
|
104
132
|
* Print the [domain] workspace header in dim style.
|
|
105
133
|
* Call at the top of every command's run() method (AUTH-04).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/lib/detect-shell.ts
|
|
3
|
+
/**
|
|
4
|
+
* Detect shell from the $SHELL environment variable.
|
|
5
|
+
* Returns 'zsh', 'bash', or null for unrecognized / unset shells.
|
|
6
|
+
*/
|
|
7
|
+
function detectShell(shellEnv) {
|
|
8
|
+
if (shellEnv.endsWith("zsh")) return "zsh";
|
|
9
|
+
if (shellEnv.endsWith("bash")) return "bash";
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
exports.detectShell = detectShell;
|
|
@@ -35,7 +35,21 @@ twentythree workspace list
|
|
|
35
35
|
twentythree workspace use company.video23.com
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
## Step 3:
|
|
38
|
+
## Step 3: Enable tab completion (optional)
|
|
39
|
+
|
|
40
|
+
Run this once to set up `<TAB>` completion for all commands and flags:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
twentythree autocomplete
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The command detects your shell (bash or zsh), builds the completion cache, and shows the eval line to paste into your RC file. After sourcing your RC file or restarting your terminal, try:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
twentythree video <TAB>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Step 4: Run your first command
|
|
39
53
|
|
|
40
54
|
List the videos in your active workspace.
|
|
41
55
|
|
|
@@ -64,3 +78,5 @@ Run `twentythree <topic> --help` for help on any topic. For example:
|
|
|
64
78
|
twentythree video --help
|
|
65
79
|
twentythree auth --help
|
|
66
80
|
```
|
|
81
|
+
|
|
82
|
+
Use `<TAB>` after any command to discover subcommands and flags.
|