Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var BaseAdvpermDisable = common.Shortcut{ Service: "base", Command: "+advperm-disable", Description: "Disable advanced permissions for a Base", Risk: "high-risk-write", Scopes: []string{"base:app:update"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, }, Tips: []string{ baseHighRiskYesTip, "Disabling advanced permissions invalidates existing custom roles; confirm the target Base before passing --yes.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). PUT("/open-apis/base/v3/bases/:base_token/advperm/enable?enable=false"). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") queryParams := make(larkcore.QueryParams) queryParams.Set("enable", "false") apiResp, err := runtime.DoAPI(&larkcore.ApiReq{ HttpMethod: http.MethodPut, ApiPath: fmt.Sprintf("/open-apis/base/v3/bases/%s/advperm/enable", validate.EncodePathSegment(baseToken)), QueryParams: queryParams, }) if err != nil { return err } return handleRoleAPIResponse(runtime, apiResp, "disable advanced permissions failed") }, }
View Source
var BaseAdvpermEnable = common.Shortcut{ Service: "base", Command: "+advperm-enable", Description: "Enable advanced permissions for a Base", Risk: "write", Scopes: []string{"base:app:update"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, }, Tips: []string{ "Caller must be a Base admin; enable advanced permissions before creating or updating roles.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). PUT("/open-apis/base/v3/bases/:base_token/advperm/enable?enable=true"). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") queryParams := make(larkcore.QueryParams) queryParams.Set("enable", "true") apiResp, err := runtime.DoAPI(&larkcore.ApiReq{ HttpMethod: http.MethodPut, ApiPath: fmt.Sprintf("/open-apis/base/v3/bases/%s/advperm/enable", validate.EncodePathSegment(baseToken)), QueryParams: queryParams, }) if err != nil { return err } return handleRoleAPIResponse(runtime, apiResp, "enable advanced permissions failed") }, }
View Source
var BaseAppBlockCreate = common.Shortcut{ Service: "base", Command: "+app-block-create", Description: "Create a block on a BaseApp page", Risk: "write", Scopes: []string{"base:appmode_block:create", "base:appmode_block:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), pageIDFlag(true), {Name: "name", Desc: "block name", Required: true}, {Name: "type", Desc: "block type: chart(column|bar|line|pie|ring|area|combo|scatter|funnel|wordCloud|radar|statistics) | text | list. Read lark-base-app-block-data-config.md before creating.", Required: true, Enum: appBlockTypes()}, {Name: "sub-type", Desc: "list subtype: standard|grouped|collapsible|card|detail; defaults to standard", Enum: appListSubTypes}, {Name: "data-config", Desc: "data_config JSON object; read lark-base-app-block-data-config.md for the SSOT"}, {Name: "no-validate", Type: "bool", Desc: "skip local data_config validation and normalization; send data_config as-is"}, }, Tips: []string{ `lark-cli base +app-block-create --app-token <app_token> --page-id <page_id> --name "Order Count" --type statistics --data-config '{"base_token":"basxxx","data_sources":[{"table_name":"Orders","count_all":true}]}'`, `lark-cli base +app-block-create --app-token <app_token> --page-id <page_id> --name "Monthly sales" --type column --data-config '{"base_token":"basxxx","data_sources":[{"table_name":"Orders","series":[{"field_name":"Amount","rollup":"SUM"}],"group_by":[{"field_name":"Month","sort":{"type":"group","order":"asc"}}]}]}'`, "Chart blocks use multi-datasource data_config: one top-level base_token shared by all sources, with table_name/series/count_all/group_by/filter inside each data_sources[] element (text needs none). App block commands carry no --base-token.", `lark-cli base +app-block-create --app-token <app_token> --page-id <page_id> --name "Notes" --type text --data-config '{"text":"# Sales overview"}'`, `lark-cli base +app-block-create --app-token <app_token> --page-id <page_id> --name "Open orders" --type list --sub-type standard --data-config '{"base_token":"basxxx","table_name":"Orders"}'`, "For list creates, omit optional columns/fields to use the product defaults. The CLI sends them only when explicitly provided.", "Before creating data-backed blocks, use +table-list and +field-list to confirm real table and field names.", "A list accepts exactly one base_token, and that Base must be in the same Workspace as the App.", "Read lark-base-app-block-data-config.md as the SSOT for chart, list and text config; do not invent data_config from natural language.", "Block type cannot be changed after creation and this phase has no delete command, so a wrong --type can only be fixed in the UI. Confirm the type before creating.", "Widget layout, position, size and display settings are not part of the public create/update protocol; the platform applies product defaults.", "Record block_id for +app-block-update. For chart data reads, pass the returned chart_token to +app-block-get-data --block-id.", "Block names must be unique within the page; the CLI checks every existing block before creation.", "Create blocks sequentially; do not parallelize multiple block creates for the same page.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { blockType := strings.TrimSpace(runtime.Str("type")) if !isAppBlockType(blockType) { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--type %q 不在支持的 block 类型内: %s", blockType, strings.Join(appBlockTypes(), ", ")).WithParam("--type") } raw := strings.TrimSpace(runtime.Str("data-config")) noValidate := runtime.Bool("no-validate") var cfg map[string]interface{} if raw != "" && !noValidate { var err error cfg, err = parseJSONObject(newParseCtx(runtime), raw, "data-config") if err != nil { return err } } if strings.EqualFold(blockType, "list") { subType, ok := normalizeAppListSubType(runtime.Str("sub-type")) if !ok { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--sub-type 仅支持 %s", strings.Join(appListSubTypes, "|")).WithParam("--sub-type") } if cfg != nil { if problems := validateAppListDataConfig(subType, cfg); len(problems) > 0 { return formatDataConfigErrors(problems) } } } else if strings.TrimSpace(runtime.Str("sub-type")) != "" { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--sub-type 仅适用于 list 类型组件").WithParam("--sub-type") } if raw == "" { if strings.EqualFold(blockType, "list") || isChartBlockType(blockType) { return errs.NewValidationError(errs.SubtypeInvalidArgument, "%s 类型组件必须提供 data-config", blockType).WithParam("--data-config") } return nil } if noValidate { return nil } norm := cfg if !strings.EqualFold(blockType, "list") { if isChartBlockType(blockType) { norm = normalizeAppChartDataConfig(cfg) } else { norm = normalizeDataConfig(cfg) } if problems := validateAppBlockDataConfig(blockType, norm); len(problems) > 0 { return formatDataConfigErrors(problems) } } b, _ := json.Marshal(norm) _ = runtime.Cmd.Flags().Set("data-config", string(b)) return nil }, DryRun: dryRunAppBlockCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeAppBlockCreate(runtime) }, }
View Source
var BaseAppBlockGet = common.Shortcut{ Service: "base", Command: "+app-block-get", Description: "Get a BaseApp page block by ID", Risk: "read", Scopes: []string{"base:appmode_block:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), pageIDFlag(true), appBlockIDFlag(true), }, Tips: []string{ "lark-cli base +app-block-get --app-token <app_token> --page-id <page_id> --block-id <block_id>", "Do not call this command for a component whose +app-block-list result has type=unsupported; the API will return an error.", "Returns WidgetDetail: widget_id, name, type, optional chart_token/list sub_type, and data_config.", "For a text block, the Markdown content is in data_config.text — read it here; text has no +app-block-get-data endpoint.", "For a chart's computed result, pass its chart_token to +app-block-get-data --block-id together with --app-token and --base-token.", "Read the current data_config here before replacing nested values with +app-block-update.", }, DryRun: dryRunAppBlockGet, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeAppBlockGet(runtime) }, }
View Source
var BaseAppBlockGetData = common.Shortcut{ Service: "base", Command: "+app-block-get-data", Description: "Get computed data for a BaseApp page chart block", Risk: "read", Scopes: []string{"base:appmode_block:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), baseTokenFlag(true), {Name: "block-id", Desc: "chart_token (cht… prefix) returned by +app-block-create, +app-block-list, or +app-block-get", Required: true}, }, Tips: []string{ "lark-cli base +app-block-get-data --app-token <app_token> --base-token <base_token> --block-id <chart_token>", "Do not call this command for a component whose +app-block-list result has type=unsupported; the API will return an error.", "--block-id must be a chart_token, not a widget_id.", "Read --base-token from the chart block data_config.base_token; do not choose an arbitrary +app-get ref key when the app references multiple Bases.", "The response uses the same computed chart data protocol as +dashboard-block-get-data.", "List and text blocks have no computed data; use +app-block-get for their metadata instead. For text specifically, +app-block-get returns the Markdown content in data_config.text.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return dryRunAppBlockGetData(ctx, runtime) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeAppBlockGetData(runtime) }, }
View Source
var BaseAppBlockList = common.Shortcut{ Service: "base", Command: "+app-block-list", Description: "List blocks on a BaseApp page", Risk: "read", Scopes: []string{"base:appmode_block:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), pageIDFlag(true), {Name: "page-size", Type: "int", Default: "20", Desc: "page size; must be positive"}, {Name: "page-token", Desc: "pagination token"}, }, Tips: []string{ "lark-cli base +app-block-list --app-token <app_token> --page-id <page_id>", "A returned component with type=unsupported can only be identified in this list; +app-block-get, +app-block-get-data, and +app-block-update do not support it and will return an error.", "Use block_id for +app-block-get/update. For chart data, pass chart_token to +app-block-get-data --block-id.", "These are page blocks, not dashboard blocks: do not pass a block_id from here to +dashboard-block-get.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "page-size", 20, 1, int(^uint(0)>>1)) return err }, DryRun: dryRunAppBlockList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeAppBlockList(runtime) }, }
View Source
var BaseAppBlockUpdate = common.Shortcut{ Service: "base", Command: "+app-block-update", Description: "Update a block on a BaseApp page", Risk: "write", Scopes: []string{"base:appmode_block:update", "base:appmode_block:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), pageIDFlag(true), appBlockIDFlag(true), {Name: "name", Desc: "new block name"}, {Name: "data-config", Desc: "data_config JSON object; read lark-base-app-block-data-config.md for the SSOT"}, {Name: "no-validate", Type: "bool", Desc: "skip local data_config normalization; send data_config as-is"}, }, Tips: []string{ `lark-cli base +app-block-update --app-token <app_token> --page-id <page_id> --block-id <block_id> --name "Monthly sales"`, `lark-cli base +app-block-update --app-token <app_token> --page-id <page_id> --block-id <block_id> --data-config '{"base_token":"basxxx","data_sources":[{"table_name":"Orders","count_all":true,"filter":{"conjunction":"and","conditions":[{"field_name":"Status","operator":"is","value":"Closed"}]}}]}'`, "Do not call this command for a component whose +app-block-list result has type=unsupported; the API will return an error.", "Read lark-base-app-block-data-config.md as the SSOT; do not invent data_config from natural language.", "Use +app-block-get first to inspect the current data_config before replacing nested values.", "The type and sub_type of an existing Block are immutable after creation and are not part of data_config; +app-block-update accepts only the name and data_config fields. If a user asks to change type/sub_type, read the current Block and always state this constraint in the final answer, even when it already matches and no write is needed; if it differs, it can only be fixed in the UI.", "Only explicitly provided data_config fields are sent; omitted fields stay unchanged. For charts, passing data_sources replaces the whole ordered array, and changing base_token requires sending the full data_sources.", "Widget layout, position, size and display settings are not part of the public create/update protocol.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { name := strings.TrimSpace(runtime.Str("name")) raw := strings.TrimSpace(runtime.Str("data-config")) if name == "" && raw == "" { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--name 与 --data-config 至少提供一个").WithParam("--name") } if runtime.Bool("no-validate") { return nil } if raw == "" { return nil } pc := newParseCtx(runtime) cfg, err := parseJSONObject(pc, raw, "data-config") if err != nil { return err } if containsJSONNull(cfg) { return formatDataConfigErrors([]string{"Update 不接受 null 作为清空标记"}) } if problems := validateAppBlockUpdateTopLevelFields(cfg); len(problems) > 0 { return formatDataConfigErrors(problems) } norm := normalizeAppChartDataConfig(cfg) if sources, exists := norm["data_sources"]; exists { items, ok := sources.([]interface{}) if !ok || len(items) == 0 { return formatDataConfigErrors([]string{"data_sources 一旦传入,必须是至少包含一项的完整有序数组"}) } var problems []string for i, rawSource := range items { source, ok := rawSource.(map[string]interface{}) if !ok { problems = append(problems, fmt.Sprintf("data_sources[%d] 必须是对象", i)) continue } for _, problem := range validateAppChartDataSourceConfig(source) { problems = append(problems, fmt.Sprintf("data_sources[%d]: %s", i, problem)) } } if len(problems) > 0 { return formatDataConfigErrors(problems) } } b, _ := json.Marshal(norm) _ = runtime.Cmd.Flags().Set("data-config", string(b)) return nil }, DryRun: dryRunAppBlockUpdate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeAppBlockUpdate(runtime) }, }
View Source
var BaseAppCreate = common.Shortcut{ Service: "base", Command: "+app-create", Description: "Create a new BaseApp in a Workspace (not a copy)", Risk: "write", Scopes: []string{ "base:appmode:create", "base:workspace:update", }, AuthTypes: authTypes(), Flags: []common.Flag{ {Name: "name", Desc: "BaseApp name", Required: true}, workspaceTokenFlag(true), {Name: "theme-style", Desc: "theme style", Enum: []string{"default", "cloudBlue", "fresh", "softLight", "future", "technology"}}, }, Tips: []string{ `lark-cli base +app-create --name "Sales app" --workspace-token <workspace_token>`, `lark-cli base +app-create --name "Sales app" --workspace-token <workspace_token> --theme-style cloudBlue`, "This command creates a new empty BaseApp; it does not copy an existing BaseApp or its pages and blocks.", "Create or select a Base separately when the app needs data.", "Record the returned app_token; page and block commands require it.", }, DryRun: dryRunBaseappCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseappCreate(runtime) }, }
View Source
var BaseAppGet = common.Shortcut{ Service: "base", Command: "+app-get", Description: "Get BaseApp info, page summaries, and the referenced Base/Table map", Risk: "read", Scopes: []string{"base:appmode:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), }, Tips: []string{ "lark-cli base +app-get --app-token <app_token>", "ref maps each Base token currently referenced by app widgets to the names of its referenced tables; table/field/record commands take the Base token keys.", "The response includes page summaries. Use +app-page-get or +app-block-list for component details.", }, DryRun: dryRunBaseappGet, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseappGet(runtime) }, }
View Source
var BaseAppPageCreate = common.Shortcut{ Service: "base", Command: "+app-page-create", Description: "Create a page in a BaseApp", Risk: "write", Scopes: []string{"base:appmode_page:create", "base:appmode_page:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), {Name: "name", Desc: "page name", Required: true}, }, Tips: []string{ `lark-cli base +app-page-create --app-token <app_token> --name "Overview"`, "Page names must be unique within an app; the CLI checks existing pages before creation.", "Record the returned page_id; every +app-block-* command needs it.", "This release creates top-level pages only; PageGroup placement is not supported.", }, DryRun: dryRunBaseappPageCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseappPageCreate(runtime) }, }
View Source
var BaseAppPageDelete = common.Shortcut{ Service: "base", Command: "+app-page-delete", Description: "Delete a BaseApp page", Risk: "high-risk-write", Scopes: []string{"base:appmode_page:delete"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), pageIDFlag(true), }, Tips: []string{ "lark-cli base +app-page-delete --app-token <app_token> --page-id <page_id> --yes", "Deleting a page also deletes its blocks and cannot be recovered; the base data behind the blocks is untouched.", baseHighRiskYesTip, }, DryRun: dryRunBaseappPageDelete, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseappPageDelete(runtime) }, }
View Source
var BaseAppPageGet = common.Shortcut{ Service: "base", Command: "+app-page-get", Description: "Get a BaseApp page by ID", Risk: "read", Scopes: []string{"base:appmode_page:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), pageIDFlag(true), }, Tips: []string{ "lark-cli base +app-page-get --app-token <app_token> --page-id <page_id>", "The response is PageDetail and always includes widget summaries. Use +app-block-list when you need paginated widget details.", }, DryRun: dryRunBaseappPageGet, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseappPageGet(runtime) }, }
View Source
var BaseAppPageList = common.Shortcut{ Service: "base", Command: "+app-page-list", Description: "List pages in a BaseApp", Risk: "read", Scopes: []string{"base:appmode_page:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), {Name: "page-size", Type: "int", Default: "20", Desc: "page size; must be positive"}, {Name: "page-token", Desc: "pagination token"}, }, Tips: []string{ "lark-cli base +app-page-list --app-token <app_token>", "Use the returned page_id for +app-page-get/update/delete and every +app-block-* command.", "If a returned page has name=\"\", the current user has no permission to that page; do not treat it as an untitled page or use it as an operation target.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "page-size", 20, 1, int(^uint(0)>>1)) return err }, DryRun: dryRunBaseappPageList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseappPageList(runtime) }, }
View Source
var BaseAppPageRename = common.Shortcut{ Service: "base", Command: "+app-page-update", Description: "Rename a BaseApp page", Risk: "write", Scopes: []string{"base:appmode_page:update", "base:appmode_page:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ appTokenFlag(true), pageIDFlag(true), {Name: "name", Desc: "new page name", Required: true}, }, Tips: []string{ `lark-cli base +app-page-update --app-token <app_token> --page-id <page_id> --name "Overview"`, "Page names must be unique within an app; the CLI excludes the current page while checking.", "Renaming does not move the page; ordering and parent stay unchanged.", }, DryRun: dryRunBaseappPageRename, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseappPageRename(runtime) }, }
View Source
var BaseBaseBlockCreate = common.Shortcut{ Service: "base", Command: "+base-block-create", Description: "Create a block", Risk: "write", Scopes: []string{"base:block:create"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "type", Desc: "resource type", Required: true, Enum: baseBlockTypeEnums}, {Name: "name", Desc: "block name", Required: true}, {Name: "parent-id", Desc: "folder block id; when omitted, create at root"}, }, Tips: []string{ "Example: lark-cli base +base-block-create --base-token <base_token> --type folder --name \"Project Docs\"", "Example: lark-cli base +base-block-create --base-token <base_token> --type table --name \"Tasks\"", "Example: lark-cli base +base-block-create --base-token <base_token> --type docx --name \"Spec\" --parent-id <folder_block_id>", "Example: lark-cli base +base-block-create --base-token <base_token> --type dashboard --name \"Metrics\"", "Example: lark-cli base +base-block-create --base-token <base_token> --type workflow --name \"Approval Flow\"", "Creates a folder, table, docx, dashboard, or workflow entry.", "Do not pass null for --parent-id. Omit it to create at the root level.", "Created resources still use their own commands for content operations, such as table/field/record/docx/dashboard/workflow commands.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateBaseBlockCreate(runtime) }, DryRun: dryRunBaseBlockCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseBlockCreate(runtime) }, }
View Source
var BaseBaseBlockDelete = common.Shortcut{ Service: "base", Command: "+base-block-delete", Description: "Delete a block", Risk: "high-risk-write", Scopes: []string{"base:block:delete"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), baseBlockIDFlag(true), }, Tips: []string{ "Example: lark-cli base +base-block-delete --base-token <base_token> --block-id <block_id> --yes", "Deletes the block identified by --block-id.", "Recursive folder deletion is not supported. If a folder is not empty, move or delete its children first.", "Different block types may have independent backing resources; deletion follows backend semantics.", "Use +base-block-list first when you need to confirm the target block id.", "If the user already explicitly confirmed this exact delete target, pass --yes without asking again.", }, DryRun: dryRunBaseBlockDelete, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseBlockDelete(runtime) }, }
View Source
var BaseBaseBlockList = common.Shortcut{ Service: "base", Command: "+base-block-list", Description: "List blocks in a base", Risk: "read", Scopes: []string{"base:block:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "type", Desc: "filter by resource type", Enum: baseBlockTypeEnums}, {Name: "parent-id", Desc: "folder block id; when omitted, list all blocks"}, }, Tips: []string{ "Example: lark-cli base +base-block-list --base-token <base_token>", "Example: lark-cli base +base-block-list --base-token <base_token> --type table", "Example: lark-cli base +base-block-list --base-token <base_token> --parent-id <folder_block_id>", `JQ crop: lark-cli base +base-block-list --base-token <base_token> | jq '.blocks[] | {type, name, block_id: .id, parent_id}'`, `JQ crop docx: lark-cli base +base-block-list --base-token <base_token> --type docx | jq '.blocks[] | {name, docx_token}'`, "Blocks are resources managed directly by the base, such as folder, table, docx, dashboard, and workflow.", "For table, dashboard, and workflow blocks, returned id is the table-id, dashboard-id, or workflow-id used by the corresponding commands.", "For docx blocks, use the returned docx_token with docx commands.", "For folder blocks, pass the returned id as --parent-id when creating, listing, or moving blocks inside that folder.", "This command returns the full backend list. It intentionally does not expose limit or offset.", "Pass --type to list only one resource type.", "Pass --parent-id to list only direct children of a folder.", "Dashboard blocks are chart/widget blocks inside a dashboard; use +dashboard-block-* for those.", }, DryRun: dryRunBaseBlockList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseBlockList(runtime) }, }
View Source
var BaseBaseBlockMove = common.Shortcut{ Service: "base", Command: "+base-block-move", Description: "Move a block", Risk: "write", Scopes: []string{"base:block:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), baseBlockIDFlag(true), {Name: "parent-id", Desc: "target folder block id; when omitted, move to root"}, {Name: "before-id", Desc: "sibling block id; move the block before this sibling in the target folder/root order"}, {Name: "after-id", Desc: "sibling block id; move the block after this sibling in the target folder/root order"}, }, Tips: []string{ "Example: lark-cli base +base-block-move --base-token <base_token> --block-id <block_id> --parent-id <folder_block_id>", "Example: lark-cli base +base-block-move --base-token <base_token> --block-id <block_id> --after-id <sibling_block_id>", "Example: lark-cli base +base-block-move --base-token <base_token> --block-id <block_id> --before-id <sibling_block_id>", "Example: lark-cli base +base-block-move --base-token <base_token> --block-id <block_id>", "Omit --parent-id to move the block to root; do not pass null.", "--before-id and --after-id are mutually exclusive.", "When moving a folder, its children remain under that folder.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateBaseBlockMove(runtime) }, DryRun: dryRunBaseBlockMove, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseBlockMove(runtime) }, }
View Source
var BaseBaseBlockRename = common.Shortcut{ Service: "base", Command: "+base-block-rename", Description: "Rename a block", Risk: "write", Scopes: []string{"base:block:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), baseBlockIDFlag(true), {Name: "name", Desc: "new unique block name; must not duplicate another block name in this base", Required: true}, }, Tips: []string{ "Example: lark-cli base +base-block-rename --base-token <base_token> --block-id <block_id> --name \"New name\"", "Renames the block identified by --block-id.", "Block names must be unique in the base; use +base-block-list first when you need to check existing names.", "Use +base-block-list first when you need to resolve the target block id from a visible name.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateBaseBlockRename(runtime) }, DryRun: dryRunBaseBlockRename, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseBlockRename(runtime) }, }
View Source
var BaseBaseCopy = common.Shortcut{ Service: "base", Command: "+base-copy", Description: "Copy a Base resource (not a BaseApp)", Risk: "write", UserScopes: []string{"base:app:copy"}, BotScopes: []string{"base:app:copy", "docs:permission.member:create"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "name", Desc: "new base name"}, {Name: "folder-token", Desc: "folder token for destination"}, {Name: "without-content", Type: "bool", Desc: "copy structure only"}, {Name: "time-zone", Desc: "time zone, e.g. Asia/Shanghai"}, }, Tips: []string{ "BaseApp/AppMode copy is unsupported. Do not pass an app_token or use this command as a substitute.", `Example: lark-cli base +base-copy --base-token <base_token> --name "Copy of Project Tracker"`, "Use --without-content when the user wants only structure.", "If copied as bot, output may include permission_grant; report it so the user knows whether they can open the new Base.", }, DryRun: dryRunBaseCopy, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseCopy(runtime) }, }
View Source
var BaseBaseCreate = common.Shortcut{ Service: "base", Command: "+base-create", Description: "Create a new base resource", Risk: "write", UserScopes: []string{ "base:app:create", "base:table:read", "base:table:create", "base:table:update", "base:table:delete", }, BotScopes: []string{ "base:app:create", "base:table:read", "base:table:create", "base:table:update", "base:table:delete", "docs:permission.member:create", }, AuthTypes: authTypes(), Flags: []common.Flag{ {Name: "name", Desc: "base name", Required: true}, {Name: "folder-token", Desc: "folder token for destination"}, {Name: "time-zone", Desc: "time zone, e.g. Asia/Shanghai"}, {Name: "fields", Desc: `field JSON array for the first table schema; use with --table-name, e.g. [{"name":"Title","type":"text"},{"name":"Status","type":"select","options":[{"name":"Todo"},{"name":"Done"}]}]`}, {Name: "table-name", Desc: "first table name for the custom first table schema; use with --fields"}, }, Tips: []string{ `Example: lark-cli base +base-create --name "Project Tracker" --time-zone Asia/Shanghai`, `Strongly recommended initial table schema: lark-cli base +base-create --name "Project Tracker" --table-name "Tasks" --fields '[{"name":"Title","type":"text"},{"name":"Status","type":"select","options":[{"name":"Todo"},{"name":"Done"}]}]'`, "Before using --fields, read lark-base-field-schema.md or rely on the same field JSON shape used by +field-create; do not invent field properties.", "If --table-name and --fields are both omitted, Base creates one initial table with the platform default schema.", "If created as bot, output may include permission_grant; report it so the user knows whether they can open the new Base.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateBaseCreate(runtime) }, DryRun: dryRunBaseCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseCreate(runtime) }, }
View Source
var BaseBaseGet = common.Shortcut{ Service: "base", Command: "+base-get", Description: "Get a base resource", Risk: "read", Scopes: []string{"base:app:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true)}, Tips: []string{ "Use a real Base token; workspace tokens and wiki tokens are not accepted by this command.", }, DryRun: dryRunBaseGet, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseGet(runtime) }, }
View Source
var BaseButtonRuleBind = common.Shortcut{ Service: "base", Command: "+button-rule-bind", Description: "Bind a button field to a workflow", Risk: "write", Scopes: []string{"base:field:read", "base:field:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true), {Name: "workflow-id", Desc: "public workflow ID returned by +workflow-create or +workflow-list (wkf prefix)", Required: true}, }, Tips: []string{ "Use this after +workflow-create and +field-create; do not put workflow_id in the field JSON.", "workflow-id must be the public wkf ID returned by workflow commands; never pass an internal numeric workflow ID.", "Binding is independent from workflow enablement. Query with +button-rule-get, then call +workflow-enable only if the user wants it active.", "If binding fails after workflow and field creation, keep both IDs and retry this command instead of recreating them.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateButtonRuleWorkflowID(runtime) }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return buttonRuleDryRun(runtime, "PUT", strings.TrimSpace(runtime.Str("workflow-id"))) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { fieldID, err := resolveButtonRuleFieldID(runtime) if err != nil { return err } body := map[string]interface{}{"workflow_id": strings.TrimSpace(runtime.Str("workflow-id"))} data, err := baseV3Call(runtime, "PUT", buttonRulePath(runtime, fieldID), nil, body) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseButtonRuleGet = common.Shortcut{ Service: "base", Command: "+button-rule-get", Description: "Get the target bound to a button field", Risk: "read", Scopes: []string{"base:field:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true), }, Tips: []string{ "Returns bound=false and target=null when the button field has no binding.", "When target.type is workflow, target.id is a public wkf ID suitable for +workflow-get, +workflow-enable, and +button-rule-bind.", "Use this after +button-rule-bind before enabling a newly created workflow.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateButtonRuleLocator(runtime) }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return buttonRuleDryRun(runtime, "GET", "") }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { fieldID, err := resolveButtonRuleFieldID(runtime) if err != nil { return err } data, err := baseV3Call(runtime, "GET", buttonRulePath(runtime, fieldID), nil, nil) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseButtonRuleUnbind = common.Shortcut{ Service: "base", Command: "+button-rule-unbind", Description: "Remove the workflow binding from a button field", Risk: "write", Scopes: []string{"base:field:read", "base:field:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true), }, Tips: []string{ "Unbind removes only the ButtonRule relation; it does not delete the field or workflow.", "Repeat unbind is safe and should leave the button field with bound=false.", "Use +button-rule-get after unbind when the agent needs readback evidence.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateButtonRuleLocator(runtime) }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return buttonRuleDryRun(runtime, "PUT", "") }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { fieldID, err := resolveButtonRuleFieldID(runtime) if err != nil { return err } data, err := baseV3Call(runtime, "PUT", buttonRulePath(runtime, fieldID), nil, map[string]interface{}{"workflow_id": ""}) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseDashboardArrange = common.Shortcut{ Service: "base", Command: "+dashboard-arrange", Description: "Auto-arrange dashboard blocks layout (server-side smart layout)", Risk: "write", Scopes: []string{"base:dashboard:update"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), {Name: "user-id-type", Desc: "user ID type: open_id / union_id / user_id"}, }, Tips: []string{ "Server-side smart layout is not deterministic or position-specific; use only when the user asks to arrange or beautify a dashboard, or to tidy up a dashboard created from scratch in this session.", "For exact placement, set --position on +dashboard-block-create / +dashboard-block-update instead; do not run this command as a substitute for coordinates the user asked for.", }, DryRun: dryRunDashboardArrange, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardArrange(runtime) }, }
View Source
var BaseDashboardBlockCreate = common.Shortcut{ Service: "base", Command: "+dashboard-block-create", Description: "Create a block in a dashboard", Risk: "write", Scopes: []string{"base:dashboard:create"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), {Name: "name", Desc: "block name", Required: true}, {Name: "type", Desc: "block type: column(柱状图)|bar(条形图)|line(折线图)|pie(饼图)|ring(环形图)|area(面积图)|combo(组合图)|scatter(散点图)|funnel(漏斗图)|wordCloud(词云)|radar(雷达图)|statistics(指标卡)|text(文本). Read lark-base-dashboard-block-config.md before creating.", Required: true}, {Name: "data-config", Desc: "data_config JSON object; read lark-base-dashboard-block-config.md for the SSOT"}, {Name: "position", Desc: `optional. component position+size in 12-col grid, JSON {"x","y","w","h"}; all four keys required and numeric (position is submitted whole, so a partial object cannot express a complete placement). Advisory bounds x/y>=0, 1<=w<=12 and x+w<=12, h>=1 — coordinate VALUES are not validated locally and pass through as given; the server auto-arranges out-of-range or overlapping positions. Omit for server auto-layout`}, {Name: "user-id-type", Desc: "user ID type for user fields in filters: open_id / union_id / user_id"}, {Name: "no-validate", Type: "bool", Desc: "skip local SEMANTIC validation: data_config checks + normalization, and the --position x/y/w/h completeness check. JSON syntax is still parsed (a malformed value never silently vanishes from the preview). Sends data_config and position as-is"}, }, Tips: []string{ `lark-cli base +dashboard-block-create --base-token <base_token> --dashboard-id <dashboard_id> --name "Order Count" --type statistics --data-config '{"table_name":"Orders","count_all":true}'`, `lark-cli base +dashboard-block-create --base-token <base_token> --dashboard-id <dashboard_id> --name "Revenue" --type statistics --data-config '{"table_name":"Orders","series":[{"field_name":"Amount","rollup":"SUM"}],"number_format":{"formatName":"dollar_rounded","precision":2}}'`, `lark-cli base +dashboard-block-create --base-token <base_token> --dashboard-id <dashboard_id> --name "Dashboard Note" --type text --data-config '{"text":"# Sales Dashboard"}'`, `lark-cli base +dashboard-block-create --base-token <base_token> --dashboard-id <dashboard_id> --name "Order Count" --type statistics --data-config '{"table_name":"Orders","count_all":true}' --position '{"x":0,"y":0,"w":6,"h":4}'`, "Before creating data-backed blocks, use +table-list and +field-list to confirm real table and field names.", "data_config uses table and field names, not table_id or field_id.", "Read lark-base-dashboard-block-config.md as the SSOT for chart templates, filters, metric rules, and type-specific fields; do not invent data_config from natural language.", "--position is optional precise layout in a 12-col grid; omit it to let the server auto-layout. Coordinate values are not validated locally; the server auto-arranges out-of-range or overlapping positions. To re-tidy an existing dashboard use +dashboard-arrange instead.", "For funnel/stage charts backed by ordered helper data, set the intended group_by.sort in the initial create request; do not create first and then issue a second update just to fix sorting.", "Record the returned block_id; block update/delete/get-data commands need it.", "Create dashboard blocks sequentially; do not parallelize multiple block creates for the same dashboard.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { pc := newParseCtx(runtime) if err := validateDashboardBlockPosition(pc, runtime); err != nil { return err } raw := strings.TrimSpace(runtime.Str("data-config")) if raw == "" { if !runtime.Bool("no-validate") && strings.EqualFold(strings.TrimSpace(runtime.Str("type")), "text") { return errs.NewValidationError(errs.SubtypeInvalidArgument, "text 类型组件必须提供 data-config,包含必填字段 text").WithParam("--data-config") } return nil } cfg, err := parseJSONObject(pc, raw, "data-config") if err != nil { return err } effective := cfg if !runtime.Bool("no-validate") { effective = normalizeDataConfig(cfg) if errs := validateBlockDataConfig(runtime.Str("type"), effective); len(errs) > 0 { return formatDataConfigErrors(errs) } } b, _ := json.Marshal(effective) _ = runtime.Cmd.Flags().Set("data-config", string(b)) return nil }, DryRun: dryRunDashboardBlockCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardBlockCreate(runtime) }, }
View Source
var BaseDashboardBlockDelete = common.Shortcut{ Service: "base", Command: "+dashboard-block-delete", Description: "Delete a dashboard block", Risk: "high-risk-write", Scopes: []string{"base:dashboard:delete"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), blockIDFlag(true), }, Tips: []string{ "lark-cli base +dashboard-block-delete --base-token <base_token> --dashboard-id <dashboard_id> --block-id <block_id> --yes", baseHighRiskYesTip, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). DELETE("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id/blocks/:block_id"). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")). Set("block_id", runtime.Str("block-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardBlockDelete(runtime) }, }
View Source
var BaseDashboardBlockGet = common.Shortcut{ Service: "base", Command: "+dashboard-block-get", Description: "Get a dashboard block by ID", Risk: "read", Scopes: []string{"base:dashboard:read"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), blockIDFlag(true), {Name: "user-id-type", Desc: "user ID type: open_id / union_id / user_id"}, }, Tips: []string{ "lark-cli base +dashboard-block-get --base-token <base_token> --dashboard-id <dashboard_id> --block-id <block_id>", "Use this command for block metadata such as name, type, layout, and data_config.", "Text block content is stored in data_config.text; include it when the user asks for all dashboard content.", "Use +dashboard-block-get-data when you need the computed chart result instead of metadata.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { params := map[string]interface{}{} if uid := strings.TrimSpace(runtime.Str("user-id-type")); uid != "" { params["user_id_type"] = uid } return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id/blocks/:block_id"). Params(params). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")). Set("block_id", runtime.Str("block-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardBlockGet(runtime) }, }
View Source
var BaseDashboardBlockGetData = common.Shortcut{ Service: "base", Command: "+dashboard-block-get-data", Description: "Get computed data for a dashboard chart block", Risk: "read", Scopes: []string{"base:dashboard:read"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), blockIDFlag(true), {Name: "dashboard-id", Desc: "hidden compatibility flag accepted by dashboard block commands; ignored by get-data", Hidden: true}, }, Tips: []string{ "lark-cli base +dashboard-block-get-data --base-token <base_token> --block-id <block_id>", "This command does not need --dashboard-id.", "Use +dashboard-block-get first when you need block metadata like name, type, or data_config.", "This command returns computed chart protocol JSON directly, not wrapped block metadata.", "For a complete dashboard export, read text blocks with +dashboard-block-get; their content is in data_config.text.", "If a chart type does not support computed data, inspect its data_config with +dashboard-block-get, then use +data-query with the same real table, dimensions, measures, and filters; do not omit the block or guess values.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return dryRunDashboardBlockGetData(ctx, runtime) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardBlockGetData(runtime) }, }
View Source
var BaseDashboardBlockList = common.Shortcut{ Service: "base", Command: "+dashboard-block-list", Description: "List blocks in a dashboard", Risk: "read", Scopes: []string{"base:dashboard:read"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), {Name: "page-size", Type: "int", Default: "20", Desc: "page size, range 1-100"}, {Name: "page-token", Desc: "pagination token"}, }, Tips: []string{ "lark-cli base +dashboard-block-list --base-token <base_token> --dashboard-id <dashboard_id>", "Use returned block_id and type values for +dashboard-block-get/update/delete/get-data.", "For a complete dashboard, use --page-size 100; while has_more=true, pass the returned page_token to --page-token and continue until has_more=false.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "page-size", 20, 1, 100) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { params := map[string]interface{}{} params["page_size"] = runtime.Int("page-size") if pt := strings.TrimSpace(runtime.Str("page-token")); pt != "" { params["page_token"] = pt } return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id/blocks"). Params(params). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardBlockList(runtime) }, }
View Source
var BaseDashboardBlockUpdate = common.Shortcut{ Service: "base", Command: "+dashboard-block-update", Description: "Update a dashboard block", Risk: "write", Scopes: []string{"base:dashboard:update"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), blockIDFlag(true), {Name: "name", Desc: "new block name"}, {Name: "data-config", Desc: "data_config JSON object; read lark-base-dashboard-block-config.md for the SSOT"}, {Name: "position", Desc: `optional. component position+size in 12-col grid, JSON {"x","y","w","h"}; all four keys required and numeric (position is submitted whole, so a partial object cannot express a complete placement). Advisory bounds x/y>=0, 1<=w<=12 and x+w<=12, h>=1 — coordinate VALUES are not validated locally and pass through as given; the server auto-arranges out-of-range or overlapping positions. Omit to leave layout unchanged`}, {Name: "user-id-type", Desc: "user ID type for user fields in filters: open_id / union_id / user_id"}, {Name: "no-validate", Type: "bool", Desc: "skip local SEMANTIC validation: data_config checks + normalization, and the --position x/y/w/h completeness check. JSON syntax is still parsed (a malformed value never silently vanishes from the preview). Sends data_config and position as-is"}, }, Tips: []string{ `lark-cli base +dashboard-block-update --base-token <base_token> --dashboard-id <dashboard_id> --block-id <block_id> --name "Total Sales"`, `lark-cli base +dashboard-block-update --base-token <base_token> --dashboard-id <dashboard_id> --block-id <block_id> --data-config '{"series":[{"field_name":"Amount","rollup":"SUM"}]}'`, `lark-cli base +dashboard-block-update --base-token <base_token> --dashboard-id <dashboard_id> --block-id <block_id> --data-config '{"number_format":{"formatName":"dollar_rounded","precision":0}}'`, `lark-cli base +dashboard-block-update --base-token <base_token> --dashboard-id <dashboard_id> --block-id <block_id> --position '{"x":6,"y":0,"w":6,"h":4}'`, "Read lark-base-dashboard-block-config.md as the SSOT for data_config templates, filters, metric rules, and type-specific fields; do not invent data_config from natural language.", "Use +dashboard-block-get first to inspect the current data_config before replacing nested values.", "Block type cannot be changed; delete and recreate the block to change chart type.", "data_config update merges top-level keys; each provided key is normally replaced as a whole, except number_format, whose subfields merge server-side.", "--position is optional precise layout in a 12-col grid; omit it to leave the current layout unchanged. Coordinate values are not validated locally; the server auto-arranges out-of-range or overlapping positions. To re-tidy an existing dashboard use +dashboard-arrange instead.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { pc := newParseCtx(runtime) if err := validateDashboardBlockPosition(pc, runtime); err != nil { return err } raw := strings.TrimSpace(runtime.Str("data-config")) if raw == "" { return nil } cfg, err := parseJSONObject(pc, raw, "data-config") if err != nil { return err } effective := cfg if !runtime.Bool("no-validate") { effective = normalizeDataConfig(cfg) if rawNumberFormat, hasNumberFormat := effective["number_format"]; hasNumberFormat { if problems := validateNumberFormat(rawNumberFormat); len(problems) > 0 { return formatDataConfigErrors(problems) } } } b, _ := json.Marshal(effective) _ = runtime.Cmd.Flags().Set("data-config", string(b)) return nil }, DryRun: dryRunDashboardBlockUpdate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardBlockUpdate(runtime) }, }
View Source
var BaseDashboardCreate = common.Shortcut{ Service: "base", Command: "+dashboard-create", Description: "Create a dashboard in a base", Risk: "write", Scopes: []string{"base:dashboard:create"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), {Name: "name", Desc: "dashboard name", Required: true}, {Name: "theme-style", Desc: "theme style, defaults to platform default when omitted"}, }, Tips: []string{ "Record the returned dashboard_id; dashboard block create/get/update/delete/arrange commands need it.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { body := map[string]interface{}{} if name := runtime.Str("name"); name != "" { body["name"] = name } if themeStyle := runtime.Str("theme-style"); themeStyle != "" { body["theme"] = map[string]interface{}{"theme_style": themeStyle} } return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/dashboards"). Body(body). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardCreate(runtime) }, }
View Source
var BaseDashboardDelete = common.Shortcut{ Service: "base", Command: "+dashboard-delete", Description: "Delete a dashboard", Risk: "high-risk-write", Scopes: []string{"base:dashboard:delete"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), }, Tips: []string{ "lark-cli base +dashboard-delete --base-token <base_token> --dashboard-id <dashboard_id> --yes", "Deleting a dashboard also deletes its blocks and cannot be recovered.", baseHighRiskYesTip, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). DELETE("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id"). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardDelete(runtime) }, }
View Source
var BaseDashboardGet = common.Shortcut{ Service: "base", Command: "+dashboard-get", Description: "Get a dashboard by ID", Risk: "read", Scopes: []string{"base:dashboard:read"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), }, Tips: []string{ "Use +dashboard-block-list or +dashboard-block-get when you need block-level details.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id"). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardGet(runtime) }, }
View Source
var BaseDashboardList = common.Shortcut{ Service: "base", Command: "+dashboard-list", Description: "List dashboards in a base", Risk: "read", Scopes: []string{"base:dashboard:read"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), {Name: "page-size", Type: "int", Default: "100", Desc: "page size, range 1-100"}, {Name: "page-token", Desc: "pagination token"}, }, Tips: []string{ "Use returned dashboard_id values for +dashboard-get, +dashboard-block-list, and +dashboard-block-create.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "page-size", 100, 1, 100) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { params := map[string]interface{}{} params["page_size"] = runtime.Int("page-size") if pt := strings.TrimSpace(runtime.Str("page-token")); pt != "" { params["page_token"] = pt } return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/dashboards"). Params(params). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardList(runtime) }, }
View Source
Service: "base", Command: "+dashboard-share-get", Description: "Get dashboard share status and settings", Risk: "read", Scopes: []string{"base:dashboard:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), }, DryRun: func(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id/share"). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")) }, Execute: func(_ context.Context, runtime *common.RuntimeContext) error { data, err := baseV3Call(runtime, "GET", baseV3Path( "bases", runtime.Str("base-token"), "dashboards", runtime.Str("dashboard-id"), "share", ), nil, nil) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
Service: "base", Command: "+dashboard-share-update", Description: "Update dashboard share status and settings", Risk: "write", Scopes: []string{"base:dashboard:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), {Name: "enabled", Type: "bool", Desc: "enable or disable dashboard sharing"}, {Name: "access-scope", Desc: "share access scope", Enum: shareAccessScopeEnums}, {Name: "show-source", Type: "bool", Desc: "show the entry back to the source Base"}, {Name: "enable-auto-analysis", Type: "bool", Desc: "enable intelligent analysis on the shared dashboard"}, }, Tips: []string{ "Boolean settings use PATCH semantics: pass --show-source=false or --enable-auto-analysis=false to explicitly turn them off.", "Update exactly one field per invocation; run separate commands to change multiple share fields.", }, Validate: func(_ context.Context, runtime *common.RuntimeContext) error { return validateSingleShareUpdate(runtime, dashboardShareUpdateFlagNames...) }, DryRun: func(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). PATCH("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id/share"). Body(buildDashboardShareUpdateBody(runtime)). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")) }, Execute: func(_ context.Context, runtime *common.RuntimeContext) error { data, err := baseV3Call(runtime, "PATCH", baseV3Path( "bases", runtime.Str("base-token"), "dashboards", runtime.Str("dashboard-id"), "share", ), nil, buildDashboardShareUpdateBody(runtime)) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseDashboardUpdate = common.Shortcut{ Service: "base", Command: "+dashboard-update", Description: "Update a dashboard", Risk: "write", Scopes: []string{"base:dashboard:update"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), dashboardIDFlag(true), {Name: "name", Desc: "new dashboard name"}, {Name: "theme-style", Desc: "theme style, leave empty to keep current theme"}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { body := map[string]interface{}{} if name := runtime.Str("name"); name != "" { body["name"] = name } if themeStyle := runtime.Str("theme-style"); themeStyle != "" { body["theme"] = map[string]interface{}{"theme_style": themeStyle} } return common.NewDryRunAPI(). PATCH("/open-apis/base/v3/bases/:base_token/dashboards/:dashboard_id"). Body(body). Set("base_token", runtime.Str("base-token")). Set("dashboard_id", runtime.Str("dashboard-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeDashboardUpdate(runtime) }, }
View Source
var BaseDataQuery = common.Shortcut{ Service: "base", Command: "+data-query", Description: "Query and analyze Base data with JSON DSL (aggregation, filter, sort)", Risk: "read", Scopes: []string{"base:table:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "dsl", Desc: "query JSON DSL; first follow lark-base-record-query-and-analysis-sop.md, then read lark-base-data-query.md only if that SOP selects +data-query", Required: true}, }, Tips: []string{ "Read lark-base-record-query-and-analysis-sop.md before using this command; use +data-query only when that SOP selects the Cloud aggregation path.", "After the SOP selects +data-query, read lark-base-data-query.md for its fewshots and DSL contract.", "`dimensions` and `measures` cannot both be empty.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { var dsl map[string]interface{} dec := json.NewDecoder(bytes.NewReader([]byte(runtime.Str("dsl")))) dec.UseNumber() if err := dec.Decode(&dsl); err != nil { return baseFlagErrorf("--dsl invalid JSON: %v", err) } _, hasDim := dsl["dimensions"] _, hasMeas := dsl["measures"] if !hasDim && !hasMeas { return baseFlagErrorf("--dsl must contain at least one of 'dimensions' or 'measures'") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { var dsl map[string]interface{} dec := json.NewDecoder(bytes.NewReader([]byte(runtime.Str("dsl")))) dec.UseNumber() dec.Decode(&dsl) return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/data/query"). Body(dsl). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") var dsl map[string]interface{} dec := json.NewDecoder(bytes.NewReader([]byte(runtime.Str("dsl")))) dec.UseNumber() dec.Decode(&dsl) data, err := baseV3Call(runtime, "POST", baseV3Path("bases", baseToken, "data/query"), nil, dsl) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseFieldCreate = common.Shortcut{ Service: "base", Command: "+field-create", Description: "Create one or more fields", Risk: "write", Scopes: []string{"base:field:create"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "json", Desc: "field property JSON object or non-empty array of field objects; supports @file", Required: true}, {Name: "i-have-read-guide", Type: "bool", Desc: "set only after you have read the formula/lookup guide for those field types", Hidden: true}, }, Tips: []string{ `Example text: lark-cli base +field-create --base-token <base_token> --table-id <table_id> --json '{"name":"Status","type":"text"}'`, `Example select: lark-cli base +field-create --base-token <base_token> --table-id <table_id> --json '{"name":"Status","type":"select","multiple":false,"options":[{"name":"Todo"},{"name":"Done"}]}'`, `+field-create defines storage schema only: choose a documented field type from explicit stored-value requirements and the user's semantics. Treat the field name or business purpose only as a clue to confirm; do not use it to invent derived behavior. Use style only to format the chosen type.`, `For explicitly requested derived, automatic, synchronized, or backfilled behavior, use documented formula, lookup, link, workflow, or automation only. If unsupported, do not probe code/web/OpenAPI, create a storage placeholder, or claim completion; report the boundary and alternatives.`, "Agent hint: for multiple fields in one table, prefer one array; array items are created sequentially.", "For generated arrays, prefer --json @file or an argv-safe subprocess call; do not double-escape JSON inside shell command substitution.", `For large arrays, bound successful stdout with --jq 'if .ok then (.data | {created,total,field_get_recommended,next_step,verification_hint}) else . end'; this preserves the full partial-failure envelope. Omit the projection when individual field IDs are needed.`, "On successful simple fields, next_step:done means stop: do not list/get fields unless readback is explicitly requested; if needed, filter +field-list with --jq instead of printing every field.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateFieldCreate(runtime) }, DryRun: dryRunFieldCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeFieldCreate(runtime) }, }
View Source
var BaseFieldDelete = common.Shortcut{ Service: "base", Command: "+field-delete", Description: "Delete a field by ID or name", Risk: "high-risk-write", Scopes: []string{"base:field:delete"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true)}, Tips: []string{ baseHighRiskYesTip, `Example: lark-cli base +field-delete --base-token <base_token> --table-id <table_id> --field-id "Status" --yes`, }, DryRun: dryRunFieldDelete, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeFieldDelete(runtime) }, }
View Source
var BaseFieldGet = common.Shortcut{ Service: "base", Command: "+field-get", Description: "Get a field by ID or name", Risk: "read", Scopes: []string{"base:field:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true)}, Tips: []string{ `Example: lark-cli base +field-get --base-token <base_token> --table-id <table_id> --field-id "Status"`, "field-id accepts a field ID (fld...) or the field name from the current table.", "Returns full field configuration; use it as the baseline before +field-update.", }, DryRun: dryRunFieldGet, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeFieldGet(runtime) }, }
View Source
var BaseFieldList = common.Shortcut{ Service: "base", Command: "+field-list", Description: "List fields in a table", Risk: "read", Scopes: []string{"base:field:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "offset", Type: "int", Default: "0", Desc: "pagination offset"}, {Name: "limit", Aliases: []string{"page-size"}, Type: "int", Default: "100", Desc: "pagination size, range 1-200"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "limit", 100, 1, 200) return err }, DryRun: dryRunFieldList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeFieldList(runtime) }, }
View Source
var BaseFieldSearchOptions = common.Shortcut{ Service: "base", Command: "+field-search-options", Description: "Search select options of a field", Risk: "read", Scopes: []string{"base:field:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true), {Name: "keyword", Desc: "keyword for option query"}, {Name: "offset", Type: "int", Default: "0", Desc: "pagination offset"}, {Name: "limit", Aliases: []string{"page-size"}, Type: "int", Default: "30", Desc: "pagination size, range 1-200"}, }, Tips: []string{ `Example: lark-cli base +field-search-options --base-token <base_token> --table-id <table_id> --field-id "Status" --keyword "Do"`, "Use only for select fields, whether multiple is false or true.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "limit", 30, 1, 200) return err }, DryRun: dryRunFieldSearchOptions, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeFieldSearchOptions(runtime) }, }
View Source
var BaseFieldUpdate = common.Shortcut{ Service: "base", Command: "+field-update", Description: "Update a field by ID or name", Risk: "high-risk-write", Scopes: []string{"base:field:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true), {Name: "json", Desc: "complete field definition JSON object; update uses full PUT semantics, not a patch", Required: true}, {Name: "i-have-read-guide", Type: "bool", Desc: "acknowledge reading formula/lookup guide before creating or updating those field types", Hidden: true}, }, Tips: []string{ baseHighRiskYesTip, `Example text: lark-cli base +field-update --base-token <base_token> --table-id <table_id> --field-id "Status" --json '{"name":"Status","type":"text"}' --yes`, `Example select: lark-cli base +field-update --base-token <base_token> --table-id <table_id> --field-id "Status" --json '{"name":"Status","type":"select","multiple":false,"options":[{"name":"Todo"},{"name":"Done"}]}' --yes`, `Example auto_number update: lark-cli base +field-update --base-token <base_token> --table-id <table_id> --field-id "编号" --json '{"name":"编号","type":"auto_number","style":{"rules":[{"type":"text","text":"TASK-"},{"type":"created_time","date_format":"yyyyMM"},{"type":"text","text":"-"},{"type":"incremental_number","length":4}]}}' --yes`, "Update uses full field-definition PUT semantics. Read the current field first with +field-get, then send the target state.", `When --json.type is "auto_number", updating the numbering rules also reapplies them to existing numbers; just submit the target field definition and do not add extra low-level parameters.`, "Type conversion is allowlist-based: only use CLI for safe conversions; otherwise migrate through a new field, or ask the user to finish high-risk conversions in the web UI.", "Formula and lookup updates require reading the corresponding guide first.", "Agent hint: use the lark-base skill's field-update guide for JSON shape, type-conversion rules, and limits.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateFieldUpdate(runtime) }, DryRun: dryRunFieldUpdate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeFieldUpdate(runtime) }, }
View Source
var BaseFormCreate = common.Shortcut{ Service: "base", Command: "+form-create", Description: "Create a form in a Base table", Risk: "write", Scopes: []string{"base:form:create"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "Base token (base_token)", Required: true}, {Name: "table-id", Desc: "table ID", Required: true}, {Name: "name", Desc: "form name", Required: true}, {Name: "description", Desc: `form description (plain text or markdown link like [text](https://example.com))`}, }, Tips: []string{ "Record the returned form_id; form question create/list/update/delete commands need it.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") name := runtime.Str("name") description := runtime.Str("description") body := map[string]interface{}{"name": name} if description != "" { body["description"] = description } data, err := baseV3Call(runtime, "POST", baseV3Path("bases", baseToken, "tables", tableId, "forms"), nil, body) if err != nil { return err } runtime.OutFormat(data, nil, func(w io.Writer) { output.PrintTable(w, []map[string]interface{}{ { "id": data["id"], "name": data["name"], "description": data["description"], }, }) }) return nil }, }
View Source
var BaseFormDelete = common.Shortcut{ Service: "base", Command: "+form-delete", Description: "Delete a form in a Base table", Risk: "high-risk-write", Scopes: []string{"base:form:delete"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, }, Tips: []string{ "Use +form-list or +form-get first when the form target is ambiguous.", baseHighRiskYesTip, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). DELETE("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") formId := runtime.Str("form-id") _, err := baseV3Call(runtime, "DELETE", baseV3Path("bases", baseToken, "tables", tableId, "forms", formId), nil, nil) if err != nil { return err } runtime.Out(map[string]interface{}{"deleted": true, "form_id": formId}, nil) return nil }, }
View Source
var BaseFormDetail = common.Shortcut{ Service: "base", Command: "+form-detail", Description: "Get form detail by share token", Risk: "read", Scopes: []string{"base:form:read"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "share-token", Desc: "Form share token (share_token)", Required: true}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/tables/forms/detail"). Body(map[string]interface{}{ "share_token": runtime.Str("share-token"), }) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { body := map[string]interface{}{ "share_token": runtime.Str("share-token"), } data, err := baseV3Call(runtime, "POST", baseV3Path("bases", "tables", "forms", "detail"), nil, body) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseFormGet = common.Shortcut{ Service: "base", Command: "+form-get", Description: "Get a form in a Base table", Risk: "read", Scopes: []string{"base:form:read"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") formId := runtime.Str("form-id") data, err := baseV3Call(runtime, "GET", baseV3Path("bases", baseToken, "tables", tableId, "forms", formId), nil, nil) if err != nil { return err } runtime.OutFormat(data, nil, func(w io.Writer) { output.PrintTable(w, []map[string]interface{}{ { "id": data["id"], "name": data["name"], "description": data["description"], }, }) }) return nil }, }
View Source
var BaseFormQuestionsCreate = common.Shortcut{ Service: "base", Command: "+form-questions-create", Description: "Create questions for a form in a Base table", Risk: "write", Scopes: []string{"base:form:update"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "Base token (base_token)", Required: true}, {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, {Name: "questions", Desc: `questions JSON array, max 10 items. Supports two shapes: create a new field question with "title"(field title) and "type"(text/number/select/datetime/user/attachment/location), or add an existing field as a question with "use_existing_field":true and "field_id"(field ID/name). Optional form fields: "description"(plain text or markdown link like [text](https://example.com)),"required","option_display_mode"(0=dropdown/1=vertical/2=horizontal,select only),"visible_rule"(display condition; same shape as view filter {"logic":"and","conditions":[["前序题目","==","是"]]}, field references another question's title/id, empty/absent = always shown). New field questions also support "multiple","options","style". E.g. '[{"type":"text","title":"Your name","required":true}]' or '[{"use_existing_field":true,"field_id":"fldEmail","title":"Email"}]'`, Required: true}, }, Tips: []string{ "If the form may already contain questions and has not been checked, run +form-questions-list for the same --base-token, --table-id, and --form-id. A verified empty form can create directly.", "New field questions create fields in the form's table; question IDs are field IDs. Use use_existing_field=true with field_id to add an existing field without creating another field.", "Unless the user explicitly requests a separate same-title question, update an existing title with +form-questions-update instead of creating a duplicate.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := parseFormQuestionsCreate(runtime.Str("questions")) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { questions, _ := parseFormQuestionsCreate(runtime.Str("questions")) return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id/questions"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")). Body(map[string]interface{}{"questions": questions}) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") formId := runtime.Str("form-id") questionsJSON := runtime.Str("questions") questions, err := parseFormQuestionsCreate(questionsJSON) if err != nil { return err } data, err := baseV3Call(runtime, "POST", baseV3Path("bases", baseToken, "tables", tableId, "forms", formId, "questions"), nil, map[string]interface{}{"questions": questions}) if err != nil { return err } items, _ := data["questions"].([]interface{}) outData := map[string]interface{}{"questions": items} runtime.OutFormat(outData, nil, func(w io.Writer) { var rows []map[string]interface{} for _, item := range items { m, _ := item.(map[string]interface{}) rows = append(rows, map[string]interface{}{ "id": m["id"], "title": m["title"], "required": m["required"], }) } output.PrintTable(w, rows) fmt.Fprintf(w, "\n%d question(s) created\n", len(items)) }) return nil }, }
View Source
var BaseFormQuestionsDelete = common.Shortcut{ Service: "base", Command: "+form-questions-delete", Description: "Delete questions from a form in a Base table", Risk: "high-risk-write", Scopes: []string{"base:form:update"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "Base token (base_token)", Required: true}, {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, {Name: "question-ids", Desc: `JSON array of question IDs (field IDs) to remove from the form, max 10 items. Default behavior also deletes the underlying fields and their record data; add --keep-field to preserve fields. E.g. '["q_001","q_002"]'`, Required: true}, {Name: "keep-field", Type: "bool", Desc: "Only remove/hide the questions from the form; keep the underlying fields and existing record data so they can be added back later with +form-questions-create using use_existing_field=true and field_id. Default false deletes fields and data."}, }, Tips: []string{ "Run +form-questions-list first and use returned question IDs; question IDs are field IDs.", "Default behavior is destructive: it deletes the underlying fields and all record data in those fields.", "Use --keep-field when you only want to remove questions from the form while preserving fields and data; those fields can be added back with +form-questions-create using use_existing_field=true and field_id.", baseHighRiskYesTip, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, _, err := buildFormQuestionsDeleteBody(runtime) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { body, _, err := buildFormQuestionsDeleteBody(runtime) if err != nil { return common.NewDryRunAPI().Set("error", err.Error()) } return common.NewDryRunAPI(). DELETE("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id/questions"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")). Body(body) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") formId := runtime.Str("form-id") body, questionIds, err := buildFormQuestionsDeleteBody(runtime) if err != nil { return err } _, err = baseV3Call(runtime, "DELETE", baseV3Path("bases", baseToken, "tables", tableId, "forms", formId, "questions"), nil, body) if err != nil { return err } runtime.Out(map[string]interface{}{ "deleted": true, "question_ids": questionIds, "keep_field": runtime.Bool("keep-field"), }, nil) return nil }, }
View Source
var BaseFormQuestionsList = common.Shortcut{ Service: "base", Command: "+form-questions-list", Description: "List questions of a form in a Base table", Risk: "read", Scopes: []string{"base:form:read"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ baseTokenFlag(true), {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, }, Tips: []string{ "Use returned question id values for +form-questions-update and +form-questions-delete.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id/questions"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") formId := runtime.Str("form-id") data, err := baseV3Call(runtime, "GET", baseV3Path("bases", baseToken, "tables", tableId, "forms", formId, "questions"), nil, nil) if err != nil { return err } items, _ := data["questions"].([]interface{}) outData := map[string]interface{}{ "questions": items, "total": data["total"], } runtime.OutFormat(outData, nil, func(w io.Writer) { if len(items) == 0 { fmt.Fprintln(w, "No questions found.") return } var rows []map[string]interface{} for _, item := range items { m, _ := item.(map[string]interface{}) rows = append(rows, map[string]interface{}{ "id": m["id"], "title": m["title"], "description": m["description"], "required": m["required"], }) } output.PrintTable(w, rows) fmt.Fprintf(w, "\n%v question(s) total\n", data["total"]) }) return nil }, }
View Source
var BaseFormQuestionsUpdate = common.Shortcut{ Service: "base", Command: "+form-questions-update", Description: "Update questions of a form in a Base table", Risk: "write", Scopes: []string{"base:form:update"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "Base token (base_token)", Required: true}, {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, {Name: "questions", Desc: `questions JSON array, max 10 items, each item must include "id". Update uses full question overwrite semantics: omitted/empty fields are written as defaults/empty, so run +form-questions-list first and include existing values you want to keep. Supported fields: "id"(required),"title","description"(plain text or markdown link like [text](https://example.com)),"required","option_display_mode"(0=dropdown,1=vertical,2=horizontal,select only),"visible_rule"(display condition; same shape as view filter {"logic":"and","conditions":[["前序题目","==","是"]]}, field references another question's title/id; pass null or omit to clear). E.g. '[{"id":"q_001","title":"Updated?","required":true}]'`, Required: true}, }, Tips: []string{ "Update uses full question overwrite semantics, not a patch.", "Run +form-questions-list first and include existing title/description/required/option_display_mode/visible_rule values you want to keep.", "Omitted fields reset to defaults; empty strings, null, and empty arrays are written as empty/clear when accepted by the API.", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { api := common.NewDryRunAPI(). PATCH("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id/questions"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")) // Transcribe the questions body verbatim so the preview shows exactly // what would be sent (including optional fields like visible_rule). var questions []interface{} if err := json.Unmarshal([]byte(runtime.Str("questions")), &questions); err == nil { api.Body(map[string]interface{}{"questions": questions}) } return api }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") formId := runtime.Str("form-id") questionsJSON := runtime.Str("questions") var questions []interface{} if err := json.Unmarshal([]byte(questionsJSON), &questions); err != nil { return baseValidationErrorf("--questions must be a valid JSON array: %s", err) } data, err := baseV3Call(runtime, "PATCH", baseV3Path("bases", baseToken, "tables", tableId, "forms", formId, "questions"), nil, map[string]interface{}{"questions": questions}) if err != nil { return err } items, _ := data["items"].([]interface{}) if len(items) == 0 { items, _ = data["questions"].([]interface{}) } outData := map[string]interface{}{"questions": items} runtime.OutFormat(outData, nil, func(w io.Writer) { var rows []map[string]interface{} for _, item := range items { m, _ := item.(map[string]interface{}) rows = append(rows, map[string]interface{}{ "id": m["id"], "title": m["title"], "required": m["required"], }) } output.PrintTable(w, rows) fmt.Fprintf(w, "\n%d question(s) updated\n", len(items)) }) return nil }, }
View Source
Service: "base", Command: "+form-share-get", Description: "Get form share status and settings", Risk: "read", Scopes: []string{"base:form:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, }, DryRun: func(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id/share"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")) }, Execute: func(_ context.Context, runtime *common.RuntimeContext) error { data, err := baseV3Call(runtime, "GET", baseV3Path( "bases", runtime.Str("base-token"), "tables", runtime.Str("table-id"), "forms", runtime.Str("form-id"), "share", ), nil, nil) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
Service: "base", Command: "+form-share-update", Description: "Update form share status and settings", Risk: "write", Scopes: []string{"base:form:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, {Name: "enabled", Type: "bool", Desc: "enable or disable form sharing"}, {Name: "access-scope", Desc: "share access scope", Enum: shareAccessScopeEnums}, {Name: "allow-anonymous", Type: "bool", Desc: "anonymize the submitter identity"}, {Name: "require-login", Type: "bool", Desc: "require submitters to sign in before submitting"}, }, Tips: []string{ "Boolean settings use PATCH semantics: pass --allow-anonymous=false or another boolean flag with =false to explicitly turn it off.", "--allow-anonymous controls submitter identity and --require-login controls sign-in; run separate update commands to change both settings.", "Update exactly one field per invocation; run separate commands to change multiple share fields.", }, Validate: func(_ context.Context, runtime *common.RuntimeContext) error { return validateFormShareUpdate(runtime) }, DryRun: func(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). PATCH("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id/share"). Body(buildFormShareUpdateBody(runtime)). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")) }, Execute: func(_ context.Context, runtime *common.RuntimeContext) error { data, err := baseV3Call(runtime, "PATCH", baseV3Path( "bases", runtime.Str("base-token"), "tables", runtime.Str("table-id"), "forms", runtime.Str("form-id"), "share", ), nil, buildFormShareUpdateBody(runtime)) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseFormSubmit = common.Shortcut{ Service: "base", Command: "+form-submit", Description: "Submit a form (fill and submit form data)", Risk: "high-risk-write", Scopes: []string{"base:form:update", "docs:document.media:upload"}, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ {Name: "share-token", Desc: "Form share token (required), extracted from the form share link", Required: true}, {Name: "base-token", Desc: "Base token (required when --json contains attachments, used for uploading attachments to Base Drive Media)"}, {Name: "json", Desc: `JSON object containing "fields" (field values) and "attachments" (attachment file paths). Example: '{"fields":{"Rating":5,"Review":"Good"},"attachments":{"Attachment":["./a.pdf","./b.png"]}}'`, Required: true}, }, Tips: []string{ `Example (no attachments): --share-token shrXXXX --json '{"fields":{"Service Rating":5,"Review":"Good service"}}'`, `Example (with attachments): --share-token shrXXXX --base-token basXXX --json '{"fields":{"Service Rating":5},"attachments":{"Attachment":["./report.pdf"]}}'`, `Cell values in "fields" follow lark-base-cell-value.md conventions; "attachments" maps field names to local file path arrays — the CLI uploads them in parallel and merges them into the submission.`, baseHighRiskYesTip, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateFormSubmit(runtime) }, DryRun: dryRunFormSubmit, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeFormSubmit(runtime) }, }
View Source
var BaseFormUpdate = common.Shortcut{ Service: "base", Command: "+form-update", Description: "Update a form in a Base table", Risk: "write", Scopes: []string{"base:form:update"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "Base token (base_token)", Required: true}, {Name: "table-id", Desc: "table ID", Required: true}, {Name: "form-id", Desc: "form ID", Required: true}, {Name: "name", Desc: "new form name"}, {Name: "description", Desc: "new form description (plain text or markdown link like [text](https://example.com))"}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). PATCH("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms/:form_id"). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")). Set("form_id", runtime.Str("form-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") formId := runtime.Str("form-id") name := runtime.Str("name") description := runtime.Str("description") body := map[string]interface{}{} if name != "" { body["name"] = name } if description != "" { body["description"] = description } data, err := baseV3Call(runtime, "PATCH", baseV3Path("bases", baseToken, "tables", tableId, "forms", formId), nil, body) if err != nil { return err } runtime.OutFormat(data, nil, func(w io.Writer) { output.PrintTable(w, []map[string]interface{}{ { "id": data["id"], "name": data["name"], "description": data["description"], }, }) }) return nil }, }
View Source
var BaseFormsList = common.Shortcut{ Service: "base", Command: "+form-list", Description: "List all forms in a Base table (auto-paginated)", Risk: "read", Scopes: []string{"base:form:read"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "Base token (base_token)", Required: true}, {Name: "table-id", Desc: "table ID", Required: true}, {Name: "page-size", Type: "int", Default: "100", Desc: "page size per request, range 1-100"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "page-size", 100, 1, 100) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/tables/:table_id/forms"). Params(map[string]interface{}{"page_size": runtime.Int("page-size")}). Set("base_token", runtime.Str("base-token")). Set("table_id", runtime.Str("table-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") tableId := runtime.Str("table-id") var allForms []interface{} pageToken := "" for { params := map[string]interface{}{ "page_size": runtime.Int("page-size"), } if pageToken != "" { params["page_token"] = pageToken } data, err := baseV3Call(runtime, "GET", baseV3Path("bases", baseToken, "tables", tableId, "forms"), params, nil) if err != nil { return err } forms, _ := data["forms"].([]interface{}) allForms = append(allForms, forms...) hasMore, _ := data["has_more"].(bool) if !hasMore { break } nextToken, _ := data["page_token"].(string) if nextToken == "" { break } pageToken = nextToken } outData := map[string]interface{}{ "forms": allForms, "total": len(allForms), } runtime.OutFormat(outData, nil, func(w io.Writer) { if len(allForms) == 0 { fmt.Fprintln(w, "No forms found.") return } var rows []map[string]interface{} for _, item := range allForms { m, _ := item.(map[string]interface{}) rows = append(rows, map[string]interface{}{ "id": m["id"], "name": m["name"], "description": m["description"], }) } output.PrintTable(w, rows) fmt.Fprintf(w, "\n%d form(s) total\n", len(allForms)) }) return nil }, }
View Source
var BaseRecordBatchCreate = common.Shortcut{ Service: "base", Command: "+record-batch-create", Description: "Batch create records", Risk: "write", Scopes: []string{"base:record:create"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "json", Desc: `batch create JSON object; create_records contains one field map per record, e.g. {"create_records":[{"Name":"Task A","Status":["Todo"]},{"Name":"Task B","Score":20}]}`, Required: true}, }, Tips: append([]string{ "Happy path field: create_records is an array of independent record field maps.", `Example: {"create_records":[{"Name":"Task A","Status":["Todo"]},{"Name":"Task B","Score":20}]}.`, "Before writing, use +field-list to confirm real writable fields; do not write system fields, formula, lookup, or attachment fields as normal CellValue.", "Batch create supports max 200 records per call.", "After batch-creating known helper rows, use the returned record IDs and your submitted rows; do not immediately +record-list the same table unless you need server-normalized formula/lookup values or failure diagnosis.", "Use the record-batch-create guide for command limits and edge cases.", }, recordCellValueHappyPathTips...), Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordJSON(runtime) }, DryRun: dryRunRecordBatchCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordBatchCreate(runtime) }, }
View Source
var BaseRecordBatchUpdate = common.Shortcut{ Service: "base", Command: "+record-batch-update", Description: "Batch update records with record-specific fields", Risk: "write", Scopes: []string{"base:record:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "json", Desc: `batch update JSON object; update_records maps each record ID to its field map, e.g. {"update_records":{"recA":{"Status":["Done"]},"recB":{"Score":20}}}`, Required: true}, }, Tips: append([]string{ "Happy path field: update_records maps each record ID to its own field map.", `Example: {"update_records":{"recA":{"Status":["Done"]},"recB":{"Score":20}}}.`, "The response contains only optional ignored_fields and does not check whether record IDs exist; read records back when confirmation is required.", "Before writing, use +field-list to confirm real writable fields; do not write system fields, formula, lookup, or attachment fields as normal CellValue.", "Batch update supports max 200 records per call; use the record-batch-update guide for command limits and edge cases.", }, recordCellValueHappyPathTips...), Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordJSON(runtime) }, DryRun: dryRunRecordBatchUpdate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordBatchUpdate(runtime) }, }
View Source
var BaseRecordDelete = common.Shortcut{ Service: "base", Command: "+record-delete", Description: "Delete one or more records by ID", Risk: "high-risk-write", Scopes: []string{"base:record:delete"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "record-id", Type: "string_array", Desc: "record ID (repeatable)"}, {Name: "json", Desc: `JSON object with record_id_list, e.g. {"record_id_list":["rec_xxx"]}`}, }, Tips: []string{ baseHighRiskYesTip, `Example: lark-cli base +record-delete --base-token <base_token> --table-id <table_id> --record-id <record_id_1> --record-id <record_id_2> --yes`, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordSelection(runtime) }, DryRun: dryRunRecordDelete, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordDelete(runtime) }, }
View Source
var BaseRecordDownloadAttachment = common.Shortcut{ Service: "base", Command: "+record-download-attachment", Description: "Download Base record attachments by record-id, optionally filtering by file-token", Risk: "read", Scopes: []string{"base:record:read", "docs:document.media:download"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), recordRefFlag(true), {Name: "file-token", Type: "string_array", Desc: "attachment file_token returned by Base; repeat to download selected files; omit to download all attachments in the record", Required: false}, {Name: "output", Desc: "local save path; with exactly one file token this may be a file path; with multiple or omitted file tokens this must be an existing directory", Required: true}, {Name: "overwrite", Type: "bool", Desc: "overwrite existing output file"}, }, Tips: []string{ `Example: lark-cli base +record-download-attachment --base-token <base_token> --table-id <table_id> --record-id <record_id> --file-token <file_token> --output ./downloads/`, `Omit --file-token to download every attachment in the record.`, `Base attachments should be downloaded with this command; other download commands may fail for Base attachment files.`, `With one --file-token, --output may be a file path or directory; with multiple or omitted --file-token values, --output must be an existing directory.`, }, DryRun: dryRunRecordDownloadAttachment, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordDownloadAttachment(runtime) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordDownloadAttachment(ctx, runtime) }, }
View Source
var BaseRecordGet = common.Shortcut{ Service: "base", Command: "+record-get", Description: "Get one or more records by ID", Risk: "read", Scopes: []string{"base:record:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "record-id", Type: "string_array", Desc: "record ID (repeatable)"}, recordProjectionFieldFlag("field ID or name to project; repeat to keep only needed columns"), recordProjectionAliasFlag("fields"), recordProjectionAliasFlag("field-names"), {Name: "json", Desc: `JSON object with record_id_list, e.g. {"record_id_list":["rec_xxx"]}`}, recordReadFormatFlag(), recordOutputFlag(), recordMinimalStdoutFlag(), recordJQRecordsFlag(), recordOverwriteFlag(), }, Normalize: normalizeRecordReadOutput, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if err := validateRecordReadFormat(runtime); err != nil { return err } if err := validateRecordExportFlags(runtime); err != nil { return err } return validateRecordSelection(runtime) }, Tips: []string{ "Example: lark-cli base +record-get --base-token <base_token> --table-id <table_id> --record-id <record_id>", "Example with projection: lark-cli base +record-get --base-token <base_token> --table-id <table_id> --record-id rec_001 --record-id rec_002 --field-id Name --field-id Status", "Example for analysis input: lark-cli base +record-get --base-token <base_token> --table-id <table_id> --record-id <record_id> --field-id <field> --format ndjson --output ./record.ndjson", recordAnalysisOutputTip, "Use --field-id as a projection boundary to avoid loading large cell values into context when they are not needed.", "Use +record-get when record_id is already known; otherwise use +record-search or +record-list.", }, DryRun: dryRunRecordGet, PostMount: func(cmd *cobra.Command) { preserveFlagOrder(cmd) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordGet(runtime) }, }
View Source
var BaseRecordHistoryList = common.Shortcut{ Service: "base", Command: "+record-history-list", Description: "List record change history", Risk: "read", Scopes: []string{"base:history:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), recordRefFlag(true), {Name: "max-version", Type: "int", Desc: "max version for next page"}, {Name: "page-size", Type: "int", Default: "30", Desc: "pagination size, range 1-50"}, }, Tips: []string{ `Example: lark-cli base +record-history-list --base-token <base_token> --table-id <table_id> --record-id <record_id>`, "This reads one record's history only; it is not a table-wide audit scan.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if _, err := common.ValidatePageSizeTyped(runtime, "page-size", 30, 1, 50); err != nil { return err } if runtime.Changed("max-version") && runtime.Int("max-version") <= 0 { return errs.NewValidationError( errs.SubtypeInvalidArgument, "--max-version must be greater than 0", ).WithParam("--max-version") } return nil }, DryRun: dryRunRecordHistoryList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { params := map[string]interface{}{ "table_id": baseTableID(runtime), "record_id": runtime.Str("record-id"), "page_size": runtime.Int("page-size"), } if value := runtime.Int("max-version"); value > 0 { params["max_version"] = value } data, err := baseV3Call(runtime, "GET", baseV3Path("bases", runtime.Str("base-token"), "record_history"), params, nil) if err != nil { return err } var pretty string if runtime.Format == "pretty" && runtime.JqExpr == "" { pretty, err = formatRecordHistoryPretty(data, time.Local) if err != nil { return err } } runtime.OutFormat(data, nil, func(w io.Writer) { _, _ = io.WriteString(w, pretty) }) return nil }, }
View Source
var BaseRecordList = common.Shortcut{ Service: "base", Command: "+record-list", Description: "List records in a table", Risk: "read", Scopes: []string{"base:record:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), recordProjectionFieldFlag("field ID or name to include; repeat to project only needed fields"), recordProjectionAliasFlag("fields"), recordProjectionAliasFlag("field-names"), recordListViewRefFlag(), recordFilterFlag(), recordSortFlag(), {Name: "offset", Type: "int", Default: "0", Desc: "pagination offset"}, {Name: "limit", Aliases: []string{"page-size"}, Type: "int", Default: "100", Desc: "maximum records to return; range 1-200, or 1-2000 for ndjson; omitted limit uses 2000 for ndjson"}, recordReadFormatFlag(), recordOutputFlag(), recordMinimalStdoutFlag(), recordJQRecordsFlag(), recordOverwriteFlag(), }, Tips: []string{ "Example: lark-cli base +record-list --base-token <base_token> --table-id <table_id> --limit 50", "Example with projection: lark-cli base +record-list --base-token <base_token> --table-id <table_id> --field-id Name --field-id Status --limit 50", "Example for analysis: lark-cli base +record-list --base-token <base_token> --table-id <table_id> --field-id Name --field-id Status --format ndjson --output ./records.ndjson", `Text equality filter: --filter-json '{"logic":"and","conditions":[["Title","==","Launch plan"]]}'`, `Text contains/like filter: --filter-json '{"logic":"and","conditions":[["Title","intersects","urgent"]]}'`, `Number equality filter: --filter-json '{"logic":"and","conditions":[["Score","==",95]]}'`, `Date equality filter: --filter-json '{"logic":"and","conditions":[["Due Date","==","ExactDate(2026-06-02)"]]}'`, `Option intersection filter: --filter-json '{"logic":"and","conditions":[["Tags","intersects",["P0","Blocked"]]]}'`, `Sort priority follows --sort-json array order: --sort-json '[{"field":"Updated","desc":true},{"field":"Title","desc":false}]'`, formatRecordQueryPriorityTip(), recordAnalysisOutputTip, "Use --field-id repeatedly to keep output small and aligned with the task.", }, Normalize: common.ChainNormalizers(normalizeRecordReadOutput, normalizeRecordNDJSONLimit), Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if err := validateRecordReadFormat(runtime); err != nil { return err } if err := validateRecordExportFlags(runtime); err != nil { return err } if err := validateRecordReadLimit(runtime, 100); err != nil { return err } if _, err := recordProjectionFields(runtime); err != nil { return err } return validateRecordQueryOptions(runtime) }, DryRun: dryRunRecordList, PostMount: func(cmd *cobra.Command) { preserveFlagOrder(cmd) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordList(runtime) }, }
View Source
var BaseRecordRemoveAttachment = common.Shortcut{ Service: "base", Command: "+record-remove-attachment", Description: "Remove one or more file_token values from a Base record attachment cell", Risk: "high-risk-write", Scopes: []string{"base:record:update", "base:field:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), recordRefFlag(true), fieldRefFlag(true), {Name: "file-token", Type: "string_array", Desc: "attachment file_token to remove from the target cell; repeat to remove multiple attachments; max 50 tokens", Required: true}, }, Tips: []string{ baseHighRiskYesTip, `Example: lark-cli base +record-remove-attachment --base-token <base_token> --table-id <table_id> --record-id <record_id> --field-id <attachment_field_id> --file-token <file_token> --yes`, `Repeat --file-token to remove multiple attachments from the same cell in one call.`, `This is a high-risk write command and requires --yes.`, }, DryRun: dryRunRecordRemoveAttachment, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordRemoveAttachment(runtime) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordRemoveAttachment(runtime) }, }
View Source
var BaseRecordSearch = common.Shortcut{ Service: "base", Command: "+record-search", Description: "Search records in a table", Risk: "read", Scopes: []string{"base:record:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "json", Desc: `record search JSON object for the full request body, e.g. {"keyword":"Alice","search_fields":["Name"],"select_fields":["Name","Status"],"filter":{"logic":"and","conditions":[]},"sort":[{"field":"Updated","desc":true}],"limit":50}; escape hatch for advanced cases`}, {Name: "keyword", Desc: "keyword for record search; required unless --json is used"}, {Name: "search-field", Type: "string_array", Desc: "field ID or name to search; repeat for multiple fields; required unless --json is used"}, recordProjectionFieldFlag("field ID or name to include; repeat to project only needed fields"), recordProjectionAliasFlag("fields"), recordProjectionAliasFlag("field-names"), recordListViewRefFlag(), recordFilterFlag(), recordSortFlag(), {Name: "offset", Type: "int", Default: "0", Desc: "pagination offset"}, {Name: "limit", Aliases: []string{"page-size"}, Type: "int", Desc: "maximum records to return; range 1-200, or 1-2000 for ndjson; omitted limit uses 10 inline or 2000 for ndjson"}, recordReadFormatFlag(), recordOutputFlag(), recordMinimalStdoutFlag(), recordJQRecordsFlag(), recordOverwriteFlag(), }, Tips: []string{ `Happy path fields: keyword (string), search_fields (1-20 field names/ids), select_fields (optional projection, <=50), view_id (optional), offset (default 0), limit (default 10 inline or 2000 for ndjson; inline range 1-200, ndjson range 1-2000).`, "JSON constraints: keyword length >=1; search_fields length 1-20; select_fields length <=50; offset >=0 defaults to 0; omitted limit uses 10 inline or 2000 for ndjson.", "view_id scopes search to records in that view; when select_fields is omitted, returned fields follow that view's visible fields.", `Example: lark-cli base +record-search --base-token <base_token> --table-id <table_id> --keyword Alice --search-field Name --field-id Name --field-id Status --limit 20`, `Example with filter/sort JSON: lark-cli base +record-search --base-token <base_token> --table-id <table_id> --keyword Alice --search-field Name --filter-json @filter.json --sort-json '[{"field":"Updated","desc":true}]'`, `Example for analysis: lark-cli base +record-search --base-token <base_token> --table-id <table_id> --keyword Alice --search-field Name --field-id Name --field-id Status --format ndjson --output ./records.ndjson`, `Text equality filter: --filter-json '{"logic":"and","conditions":[["Title","==","Launch plan"]]}'`, `Text contains/like filter: --filter-json '{"logic":"and","conditions":[["Title","intersects","urgent"]]}'`, `Option intersection filter: --filter-json '{"logic":"and","conditions":[["Tags","intersects",["P0","Blocked"]]]}'`, `Sort priority follows --sort-json array order.`, formatRecordQueryPriorityTip(), "Use +record-search for keyword matching; use --filter-json for structured conditions and --sort-json for result ordering.", "Use --json only when you need to pass the full search body directly.", recordAnalysisOutputTip, }, Normalize: normalizeRecordSearchOutput, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordSearchFlags(runtime) }, DryRun: dryRunRecordSearch, PostMount: func(cmd *cobra.Command) { preserveFlagOrder(cmd) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordSearch(runtime) }, }
View Source
Service: "base", Command: "+record-share-link-create", Description: "Generate share links for one or more records (max 100 per request)", Risk: "read", Scopes: []string{"base:record:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "record-ids", Type: "string_slice", Desc: "record IDs to generate share links for (comma-separated or repeatable, max 100)", Required: true}, }, Tips: []string{ `Example: lark-cli base +record-share-link-create --base-token <base_token> --table-id <table_id> --record-ids <record_id>`, "Max 100 record IDs per call; duplicate IDs are ignored.", "Output record_share_links maps record_id to URL; records without permission or missing records may be absent.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordShareBatch(runtime) }, DryRun: dryRunRecordShareBatch, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordShareBatch(runtime) }, }
View Source
var BaseRecordUploadAttachment = common.Shortcut{ Service: "base", Command: "+record-upload-attachment", Description: "Upload one or more local files and append the returned file_token values to a Base attachment cell", Risk: "write", Scopes: []string{"base:record:update", "base:field:read", "docs:document.media:upload"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), recordRefFlag(true), fieldRefFlag(true), {Name: "file", Type: "string_array", Desc: "local file path; repeat to append multiple attachments in one cell; max 50 files, max 2GB each; files > 20MB use multipart upload automatically", Required: true}, {Name: "name", Desc: "deprecated; attachment names are derived from local file basenames", Hidden: true}, }, Tips: []string{ `Example: lark-cli base +record-upload-attachment --base-token <base_token> --table-id <table_id> --record-id <record_id> --field-id <attachment_field_id> --file ./report.pdf`, `Repeat --file to append multiple attachments: --file ./report.pdf --file ./screenshot.png`, `Reuse returned file_token values for download/remove`, }, DryRun: dryRunRecordUploadAttachment, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordUploadAttachment(runtime) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordUploadAttachment(runtime) }, }
View Source
var BaseRecordUpsert = common.Shortcut{ Service: "base", Command: "+record-upsert", Description: "Create or update a record", Risk: "write", Scopes: []string{"base:record:create", "base:record:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), recordRefFlag(false), {Name: "json", Desc: `record field map JSON object, e.g. {"Name":"Alice","Status":["Todo"]}; do not wrap in fields`, Required: true}, }, Tips: append([]string{ "Happy path JSON is a top-level field map: each key is a real field name or field ID, each value is that field's CellValue.", "Without --record-id this creates a record; with --record-id this updates that record. It does not auto-upsert by business key.", "Before writing, use +field-list to confirm real writable fields; do not write system fields, formula, lookup, or attachment fields as normal CellValue.", "Sub-record/child-record path: when a one-way/two-way link field represents hierarchy, create a normal record and set that link field to a parent record reference array, e.g. {\"Parent Link\":[{\"id\":\"rec_xxx\"}]}; do not look for parent_record_id or a separate child-record API.", }, recordCellValueHappyPathTips...), Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateRecordJSON(runtime) }, DryRun: dryRunRecordUpsert, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordUpsert(runtime) }, }
View Source
var BaseRoleCreate = common.Shortcut{ Service: "base", Command: "+role-create", Description: "Create a custom role in a Base", Risk: "write", Scopes: []string{"base:role:create"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "json", Desc: "role config JSON; read lark-base-advanced-permission-and-role.md and lark-base-role-config.md before constructing permissions", Required: true}, }, Tips: []string{ "Requires advanced permissions to be enabled and the caller to be a Base admin.", "Use lark-base-advanced-permission-and-role.md as the module entry and lark-base-role-config.md as the role permission schema.", "Create supports custom_role only.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } var body map[string]any if err := json.Unmarshal([]byte(runtime.Str("json")), &body); err != nil { return baseFlagErrorf("--json must be valid JSON: %v", err) } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { var body map[string]any json.Unmarshal([]byte(runtime.Str("json")), &body) return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/roles"). Body(body). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") var body map[string]any json.Unmarshal([]byte(runtime.Str("json")), &body) apiResp, err := runtime.DoAPI(&larkcore.ApiReq{ HttpMethod: http.MethodPost, ApiPath: fmt.Sprintf("/open-apis/base/v3/bases/%s/roles", validate.EncodePathSegment(baseToken)), Body: body, }) if err != nil { return err } return handleRoleAPIResponse(runtime, apiResp, "create role failed") }, }
View Source
var BaseRoleDelete = common.Shortcut{ Service: "base", Command: "+role-delete", Description: "Delete a custom role (system roles cannot be deleted)", Risk: "high-risk-write", Scopes: []string{"base:role:delete"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "role-id", Desc: "role ID (e.g. rolxxxxxx4)", Required: true}, }, Tips: []string{ baseHighRiskYesTip, "Requires advanced permissions to be enabled and the caller to be a Base admin.", "Only custom roles can be deleted; system roles cannot be deleted.", "Use +role-get first if the role target is ambiguous, then pass --yes to confirm deletion.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } if strings.TrimSpace(runtime.Str("role-id")) == "" { return baseFlagErrorf("--role-id must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). DELETE("/open-apis/base/v3/bases/:base_token/roles/:role_id"). Set("base_token", runtime.Str("base-token")). Set("role_id", runtime.Str("role-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") roleId := runtime.Str("role-id") apiResp, err := runtime.DoAPI(&larkcore.ApiReq{ HttpMethod: http.MethodDelete, ApiPath: fmt.Sprintf("/open-apis/base/v3/bases/%s/roles/%s", validate.EncodePathSegment(baseToken), validate.EncodePathSegment(roleId)), Body: map[string]any{}, }) if err != nil { return err } return handleRoleAPIResponse(runtime, apiResp, "delete role failed") }, }
View Source
var BaseRoleGet = common.Shortcut{ Service: "base", Command: "+role-get", Description: "Get full config of a role", Risk: "read", Scopes: []string{"base:role:read"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "role-id", Desc: "role ID (e.g. rolxxxxxx4)", Required: true}, }, Tips: []string{ "Requires advanced permissions to be enabled and the caller to be a Base admin.", "Use before +role-update to inspect the current full permission config.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } if strings.TrimSpace(runtime.Str("role-id")) == "" { return baseFlagErrorf("--role-id must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/roles/:role_id"). Set("base_token", runtime.Str("base-token")). Set("role_id", runtime.Str("role-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") roleId := runtime.Str("role-id") apiResp, err := runtime.DoAPI(&larkcore.ApiReq{ HttpMethod: http.MethodGet, ApiPath: fmt.Sprintf("/open-apis/base/v3/bases/%s/roles/%s", validate.EncodePathSegment(baseToken), validate.EncodePathSegment(roleId)), }) if err != nil { return err } return handleRoleAPIResponse(runtime, apiResp, "get role failed") }, }
View Source
var BaseRoleList = common.Shortcut{ Service: "base", Command: "+role-list", Description: "List all roles in a Base", Risk: "read", Scopes: []string{"base:role:read"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, }, Tips: []string{ "Requires advanced permissions to be enabled and the caller to be a Base admin.", "Returns role summaries; use +role-get for the full permission config.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/roles"). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") apiResp, err := runtime.DoAPI(&larkcore.ApiReq{ HttpMethod: http.MethodGet, ApiPath: fmt.Sprintf("/open-apis/base/v3/bases/%s/roles", validate.EncodePathSegment(baseToken)), }) if err != nil { return err } return handleRoleAPIResponse(runtime, apiResp, "list roles failed") }, }
View Source
var BaseRoleUpdate = common.Shortcut{ Service: "base", Command: "+role-update", Description: "Update a role config (delta merge, only changed fields needed)", Risk: "high-risk-write", Scopes: []string{"base:role:update"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "role-id", Desc: "role ID (e.g. rolxxxxxx4)", Required: true}, {Name: "json", Desc: "delta role config JSON; read lark-base-advanced-permission-and-role.md and lark-base-role-config.md before changing permissions", Required: true}, }, Tips: []string{ baseHighRiskYesTip, "Requires advanced permissions to be enabled and the caller to be a Base admin.", "Update is a delta merge: only changed fields are updated, others remain unchanged.", "Use lark-base-advanced-permission-and-role.md as the module entry and lark-base-role-config.md as the role permission schema.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } if strings.TrimSpace(runtime.Str("role-id")) == "" { return baseFlagErrorf("--role-id must not be blank") } var body map[string]any if err := json.Unmarshal([]byte(runtime.Str("json")), &body); err != nil { return baseFlagErrorf("--json must be valid JSON: %v", err) } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { var body map[string]any json.Unmarshal([]byte(runtime.Str("json")), &body) return common.NewDryRunAPI(). Desc("Delta merge: only changed fields are updated, others remain unchanged"). PUT("/open-apis/base/v3/bases/:base_token/roles/:role_id"). Body(body). Set("base_token", runtime.Str("base-token")). Set("role_id", runtime.Str("role-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { baseToken := runtime.Str("base-token") roleId := runtime.Str("role-id") var body map[string]any json.Unmarshal([]byte(runtime.Str("json")), &body) apiResp, err := runtime.DoAPI(&larkcore.ApiReq{ HttpMethod: http.MethodPut, ApiPath: fmt.Sprintf("/open-apis/base/v3/bases/%s/roles/%s", validate.EncodePathSegment(baseToken), validate.EncodePathSegment(roleId)), Body: body, }) if err != nil { return err } return handleRoleAPIResponse(runtime, apiResp, "update role failed") }, }
View Source
var BaseTableCopy = common.Shortcut{ Service: "base", Command: "+table-copy", Description: "Copy a table by ID or name; structure only by default", Risk: "write", Scopes: []string{tableCopyScope}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "name", Desc: "target table name", Required: true}, {Name: "range", Default: tableCopyRangeSchema, Desc: "copy range; defaults to schema, use all only to include records", Enum: []string{tableCopyRangeSchema, tableCopyRangeAll}}, {Name: "wait", Type: "bool", Desc: "wait for an all-range copy task to finish"}, }, Tips: []string{ `Example: lark-cli base +table-copy --base-token <base_token> --table-id "Tasks" --name "Tasks copy"`, "table-id accepts a table ID or name in the current Base.", "The default copies schema only; use --range all only when records must also be copied.", "Use --wait with --range all to wait locally; otherwise continue with the returned next_command.", }, DryRun: dryRunTableCopy, PostMount: func(cmd *cobra.Command) { cmd.Flags().Duration("timeout", 5*time.Minute, "maximum time to wait for an asynchronous copy task (max 30m)") }, Validate: validateTableCopy, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTableCopy(ctx, runtime) }, }
View Source
var BaseTableCopyStatus = common.Shortcut{ Service: "base", Command: "+table-copy-status", Description: "Get one table copy task status", Risk: "read", Scopes: []string{tableCopyScope}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "task-id", Desc: "opaque table copy task ID", Required: true}, }, Tips: []string{ "Use the opaque task_id returned by base +table-copy; this command queries status once.", "If state is init or process, run the returned next_command later.", }, DryRun: dryRunTableCopyStatus, Validate: func(_ context.Context, runtime *common.RuntimeContext) error { taskID := runtime.Str("task-id") if strings.TrimSpace(taskID) == "" { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--task-id cannot be blank").WithParam("--task-id") } if len(taskID) > tableCopyTaskIDMax { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--task-id must not exceed %d bytes", tableCopyTaskIDMax).WithParam("--task-id") } return nil }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTableCopyStatus(ctx, runtime) }, }
View Source
var BaseTableCreate = common.Shortcut{ Service: "base", Command: "+table-create", Description: "Create a table with an explicit field schema, plus optional views", Risk: "write", Scopes: []string{"base:table:create", "base:field:read", "base:field:create", "base:field:update", "base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "name", Desc: "table name", Required: true}, {Name: "view", Desc: "view JSON object/array for create"}, {Name: "fields", Required: true, Desc: `field JSON array defining the table schema; must hold at least one field, e.g. [{"name":"Title","type":"text"},{"name":"Status","type":"select","multiple":false,"options":[{"name":"Todo"},{"name":"Done"}]}]`}, }, Tips: []string{ "Before using --fields, read lark-base-field-schema.md or rely on the same field JSON shape used by +field-create; do not invent field properties.", "The first --fields item becomes the primary field.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateTableCreate(runtime) }, DryRun: dryRunTableCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTableCreate(runtime) }, }
BaseTableCreate creates a table with an explicit schema. --fields is required at the cli surface (cobra MarkFlagRequired); a missing flag fails before Validate runs with cobra's standard "required flag(s)" error (which the dispatcher classifies as a typed *errs.ValidationError). validateTableCreate still rejects blank, non-array and empty-array values, because cobra accepts --fields "" and --fields "[]" — both of which would reach the API without a fields body and get the platform default schema instead of the caller's.
View Source
var BaseTableDelete = common.Shortcut{ Service: "base", Command: "+table-delete", Description: "Delete a table by ID or name", Risk: "high-risk-write", Scopes: []string{"base:table:delete"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true)}, Tips: []string{ `Example: lark-cli base +table-delete --base-token <base_token> --table-id "Old Tasks" --yes`, "table-id accepts a table ID (tbl...) or the table name in the current Base.", baseHighRiskYesTip, }, DryRun: dryRunTableDelete, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTableDelete(runtime) }, }
View Source
var BaseTableGet = common.Shortcut{ Service: "base", Command: "+table-get", Description: "Get a table by ID or name", Risk: "read", Scopes: []string{"base:table:read", "base:field:read", "base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true)}, Tips: []string{ `Example: lark-cli base +table-get --base-token <base_token> --table-id "Tasks"`, "table-id accepts a table ID (tbl...) or the table name in the current Base.", }, DryRun: dryRunTableGet, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTableGet(runtime) }, }
View Source
var BaseTableList = common.Shortcut{ Service: "base", Command: "+table-list", Description: "List tables in a base", Risk: "read", Scopes: []string{"base:table:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), {Name: "offset", Type: "int", Default: "0", Desc: "pagination offset"}, {Name: "limit", Aliases: []string{"page-size"}, Type: "int", Default: "50", Desc: "pagination size, range 1-100"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "limit", 50, 1, 100) return err }, DryRun: dryRunTableList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTableList(runtime) }, }
View Source
var BaseTableUpdate = common.Shortcut{ Service: "base", Command: "+table-update", Description: "Rename a table by ID or name", Risk: "write", Scopes: []string{"base:table:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "name", Desc: "new table name", Required: true}, }, DryRun: dryRunTableUpdate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTableUpdate(runtime) }, }
View Source
var BaseTemplateCategories = common.Shortcut{ Service: "base", Command: "+template-categories", Description: "List Base template center categories", Risk: "read", Scopes: []string{templateReadScope}, AuthTypes: authTypes(), Tips: []string{ "Use this first when the user asks to browse template categories.", `Example: lark-cli base +template-categories --as user`, }, DryRun: dryRunTemplateCategories, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTemplateCategories(runtime) }, }
View Source
var BaseTemplateList = common.Shortcut{ Service: "base", Command: "+template-list", Description: "List Base templates by category", Risk: "read", Scopes: []string{templateReadScope}, AuthTypes: authTypes(), Flags: append([]common.Flag{ {Name: "category-key", Desc: "template category key; omit to list the recommended category"}, }, templatePaginationFlags()...), Tips: []string{ "Use --category-key with a key returned by +template-categories; omit it to read the recommended category.", "Returned template.token is the Base template token. To create from it, run +base-copy --base-token <token>.", `Example: lark-cli base +template-list --category-key office --limit 10 --as user`, }, Validate: validateTemplatePagination, DryRun: dryRunTemplateList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTemplateList(runtime) }, }
View Source
var BaseTemplateSearch = common.Shortcut{ Service: "base", Command: "+template-search", Description: "Search Base templates by keyword", Risk: "read", Scopes: []string{templateReadScope}, AuthTypes: authTypes(), Flags: append([]common.Flag{ {Name: "keyword", Required: true, Desc: "template keyword; empty search is not supported"}, }, templatePaginationFlags()...), Tips: []string{ "Use this when the user wants to create a new Base and has no owned/recent Base anchor.", "Do not use drive +search for marketplace templates; drive search only finds user-accessible Drive/Wiki objects.", "Returned template.token is the Base template token. To create from it, run +base-copy --base-token <token>.", `Example: lark-cli base +template-search --keyword "project management" --limit 10 --as user`, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("keyword")) == "" { return errs.NewValidationError(errs.SubtypeInvalidArgument, "template keyword must not be blank").WithParam("--keyword") } return validateTemplatePagination(ctx, runtime) }, DryRun: dryRunTemplateSearch, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeTemplateSearch(runtime) }, }
View Source
var BaseTitleResolve = common.Shortcut{ Service: "base", Command: "+title-resolve", Description: "Resolve a Base title or keyword through Drive search", Risk: "read", Scopes: []string{"search:docs:read"}, AuthTypes: []string{"user"}, HasFormat: true, Flags: []common.Flag{ {Name: "title", Aliases: []string{"query", "url"}, Desc: "Base title keyword to search via Drive (30 characters or fewer)"}, }, Tips: []string{ `Example: lark-cli base +title-resolve --title "Sales pipeline"`, "Pass a short keyword from the Base title, 30 characters or fewer. Use +url-resolve for URLs.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := readTitleResolveQuery(runtime) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { query, err := readTitleResolveQuery(runtime) if err != nil { return common.NewDryRunAPI().Set("error", err.Error()) } return common.NewDryRunAPI(). POST("/open-apis/search/v2/doc_wiki/search"). Body(buildTitleResolveSearchBody(query)) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseTitleResolve(runtime) }, }
View Source
var BaseURLResolve = common.Shortcut{ Service: "base", Command: "+url-resolve", Description: "Resolve a Base or BaseApp URL into usable coordinates", Risk: "read", Scopes: []string{}, ConditionalScopes: []string{ "base:block:read", "base:field:read", "base:record:read", "wiki:node:retrieve", }, AuthTypes: authTypes(), HasFormat: true, Flags: []common.Flag{ {Name: "url", Aliases: []string{"query"}, Desc: "Base/BaseApp/Wiki/record-share URL to resolve"}, }, Tips: []string{ `Example: lark-cli base +url-resolve --url "https://example.larkoffice.com/base/<base_token>?table=<table_id>&view=<view_id>"`, `BaseApp example: lark-cli base +url-resolve --url "https://example.larkoffice.com/app/<app_token>?pre_pathname=/base/workspace/<workspace_token>&pageId=<page_id>"`, "Only URLs are accepted. For Base titles or keywords, use +title-resolve --title.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := readURLResolveInput(runtime) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { raw, err := readURLResolveInput(runtime) if err != nil { return common.NewDryRunAPI().Set("error", err.Error()) } parsed, err := parseResolveURL(raw) if err != nil { return common.NewDryRunAPI().Set("error", err.Error()) } switch classifyBaseURL(parsed) { case "base_url": baseToken := firstPathSegmentAfter(parsed.Path, "/base/") if selectedBlockID := strings.TrimSpace(parsed.Query().Get("table")); selectedBlockID != "" { return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/blocks/list"). Body(map[string]interface{}{}). Set("base_token", baseToken). Set("selected_block_id", selectedBlockID) } return common.NewDryRunAPI().Set("url", raw).Set("resolution", "local") case "wiki_url": dry := common.NewDryRunAPI() selectedBlockID := strings.TrimSpace(parsed.Query().Get("table")) if selectedBlockID == "" { return dry. GET("/open-apis/wiki/v2/spaces/get_node"). Params(map[string]interface{}{"token": firstPathSegmentAfter(parsed.Path, "/wiki/")}) } dry.Desc("2-step: resolve the Wiki node to a Base, then identify the selected Base block") dry.GET("/open-apis/wiki/v2/spaces/get_node"). Desc("[1] Resolve the Wiki node to its underlying Base"). Params(map[string]interface{}{"token": firstPathSegmentAfter(parsed.Path, "/wiki/")}) dry.POST("/open-apis/base/v3/bases/:base_token/blocks/list"). Desc("[2] List Base blocks and match selected_block_id"). Body(map[string]interface{}{}) return dry. Set("base_token", "<obj_token from step 1>"). Set("selected_block_id", selectedBlockID) case "record_share_url": return common.NewDryRunAPI(). GET("/open-apis/base/v3/record_share/:record_share_token/meta"). Set("record_share_token", firstPathSegmentAfter(parsed.Path, "/record/")) default: return common.NewDryRunAPI().Set("url", raw).Set("resolution", "local") } }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeBaseURLResolve(runtime) }, }
View Source
var BaseViewCreate = common.Shortcut{ Service: "base", Command: "+view-create", Description: "Create one or more views", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "json", Desc: "view JSON object/array; type defaults to grid; type range: grid, kanban, gallery, calendar, gantt", Required: true}, }, Tips: []string{ `Example: lark-cli base +view-create --base-token <base_token> --table-id <table_id> --json '{"name":"Main","type":"grid"}'`, `Minimal: --json '{"name":"Main"}' creates a grid view.`, "Do not pass form as a view type; form views are managed through form commands.", `Use +view-set-visible-fields after creation when the user needs a specific field order or visibility.`, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateViewCreate(runtime) }, DryRun: dryRunViewCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewCreate(runtime) }, }
View Source
var BaseViewDelete = common.Shortcut{ Service: "base", Command: "+view-delete", Description: "Delete a view by ID or name", Risk: "high-risk-write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, Tips: []string{ baseHighRiskYesTip, `Example: lark-cli base +view-delete --base-token <base_token> --table-id <table_id> --view-id "Old View" --yes`, }, DryRun: dryRunViewDelete, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewDelete(runtime) }, }
View Source
var BaseViewGet = common.Shortcut{ Service: "base", Command: "+view-get", Description: "Get a view by ID or name", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, DryRun: dryRunViewGet, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewGet(runtime) }, }
View Source
var BaseViewGetCard = common.Shortcut{ Service: "base", Command: "+view-get-card", Description: "Get view card configuration", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, DryRun: dryRunViewGetCard, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewGetProperty(runtime, "card", "card") }, }
View Source
var BaseViewGetFilter = common.Shortcut{ Service: "base", Command: "+view-get-filter", Description: "Get view filter configuration", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, DryRun: dryRunViewGetFilter, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewGetProperty(runtime, "filter", "filter") }, }
View Source
var BaseViewGetGroup = common.Shortcut{ Service: "base", Command: "+view-get-group", Description: "Get view group configuration", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, DryRun: dryRunViewGetGroup, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewGetProperty(runtime, "group", "group") }, }
View Source
var BaseViewGetSort = common.Shortcut{ Service: "base", Command: "+view-get-sort", Description: "Get view sort configuration", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, DryRun: dryRunViewGetSort, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewGetProperty(runtime, "sort", "sort") }, }
View Source
var BaseViewGetTimebar = common.Shortcut{ Service: "base", Command: "+view-get-timebar", Description: "Get view timebar configuration", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, DryRun: dryRunViewGetTimebar, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewGetProperty(runtime, "timebar", "timebar") }, }
View Source
var BaseViewGetVisibleFields = common.Shortcut{ Service: "base", Command: "+view-get-visible-fields", Description: "Get view visible fields configuration", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true)}, DryRun: dryRunViewGetVisibleFields, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewGetProperty(runtime, "visible_fields", "visible_fields") }, }
View Source
var BaseViewList = common.Shortcut{ Service: "base", Command: "+view-list", Description: "List views in a table", Risk: "read", Scopes: []string{"base:view:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), {Name: "offset", Type: "int", Default: "0", Desc: "pagination offset"}, {Name: "limit", Aliases: []string{"page-size"}, Type: "int", Default: "100", Desc: "pagination size, range 1-200"}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { _, err := common.ValidatePageSizeTyped(runtime, "limit", 100, 1, 200) return err }, DryRun: dryRunViewList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewList(runtime) }, }
View Source
var BaseViewRename = common.Shortcut{ Service: "base", Command: "+view-rename", Description: "Rename a view by ID or name", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true), {Name: "name", Desc: "new view name", Required: true}, }, DryRun: dryRunViewRename, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewRename(runtime) }, }
View Source
var BaseViewSetCard = common.Shortcut{ Service: "base", Command: "+view-set-card", Description: "Set view card configuration", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true), {Name: "json", Desc: `card JSON object, e.g. {"cover_field":"Cover"} or {"cover_field":null} to clear`, Required: true}, }, Tips: []string{ "Supported view types: gallery, kanban.", "cover_field should be an attachment field id/name, or null to clear.", "Use +view-get-card first when updating an existing card view configuration.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateViewJSONObject(runtime) }, DryRun: dryRunViewSetCard, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewSetJSONObject(runtime, "card", "card") }, }
View Source
var BaseViewSetFilter = common.Shortcut{ Service: "base", Command: "+view-set-filter", Description: "Set view filter configuration", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true), {Name: "json", Desc: `filter JSON object, e.g. {"logic":"and","conditions":[["Status","==","Todo"]]}`, Required: true}, }, Tips: []string{ "Agent hint: use the lark-base skill's view-set-filter guide for usage and limits.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateViewJSONObject(runtime) }, DryRun: dryRunViewSetFilter, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewSetJSONObject(runtime, "filter", "filter") }, }
View Source
var BaseViewSetGroup = common.Shortcut{ Service: "base", Command: "+view-set-group", Description: "Set view group configuration", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true), {Name: "json", Desc: `group JSON object with group_config array, e.g. {"group_config":[{"field":"Status","desc":false}]}; use {"group_config":[]} to clear`, Required: true}, }, Tips: []string{ "Supported view types: grid, kanban, gantt.", "Use a JSON object, not a bare array; grouping fields must be supported by the current view.", "group_config supports max 3 group items.", "Use +view-get-group first when modifying an existing grouping configuration.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateViewJSONObject(runtime) }, DryRun: dryRunViewSetGroup, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewSetWrapped(runtime, "group", "group_config", "group") }, }
View Source
var BaseViewSetSort = common.Shortcut{ Service: "base", Command: "+view-set-sort", Description: "Set view sort configuration", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true), {Name: "json", Desc: `sort_config JSON object, e.g. {"sort_config":[{"field":"Priority","desc":true}]}; use {"sort_config":[]} to clear; max 10 items`, Required: true}, }, Tips: []string{ "Supported view types: grid, kanban, gallery, gantt.", "Use a JSON object, not a bare array; sorting fields must be supported by the current view.", "sort_config supports max 10 sort items.", "Use +view-get-sort first when modifying an existing sort configuration.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateViewJSONObject(runtime) }, DryRun: dryRunViewSetSort, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewSetWrapped(runtime, "sort", "sort_config", "sort") }, }
View Source
var BaseViewSetTimebar = common.Shortcut{ Service: "base", Command: "+view-set-timebar", Description: "Set view timebar configuration", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true), {Name: "json", Desc: `timebar JSON object with start_time, end_time, title, e.g. {"start_time":"Start Date","end_time":"End Date","title":"Name"}`, Required: true}, }, Tips: []string{ "Supported view types: calendar, gantt.", "start_time, end_time, and title are required; use date/time fields for start_time and end_time.", "Use +view-get-timebar first when modifying an existing timebar configuration.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateViewJSONObject(runtime) }, DryRun: dryRunViewSetTimebar, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewSetJSONObject(runtime, "timebar", "timebar") }, }
View Source
var BaseViewSetVisibleFields = common.Shortcut{ Service: "base", Command: "+view-set-visible-fields", Description: "Set view visible fields", Risk: "write", Scopes: []string{"base:view:write_only"}, AuthTypes: authTypes(), Flags: []common.Flag{ baseTokenFlag(true), tableRefFlag(true), viewRefFlag(true), {Name: "json", Desc: `visible fields JSON object, e.g. {"visible_fields":["Name","Status"]}`, Required: true}, }, Tips: []string{ "Supported view types: grid, kanban, gallery, calendar, gantt.", "Use a JSON object, not a bare array; primary field may be forced to the first position by the API.", "visible_fields controls both visibility and order; include every field that should remain visible.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateViewJSONObject(runtime) }, DryRun: dryRunViewSetVisibleFields, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeViewSetVisibleFields(runtime) }, }
View Source
var BaseWorkflowCreate = common.Shortcut{ Service: "base", Command: "+workflow-create", Description: "Create a new workflow in a base", Risk: "write", Scopes: []string{"base:workflow:create"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "json", Desc: "workflow body JSON; read lark-base-workflow.md and lark-base-workflow-schema.md before constructing steps", Required: true}, }, Tips: []string{ "lark-cli base +workflow-create --base-token <base_token> --json @workflow.json", "client_token is required and should be unique per create request.", "New workflows are created disabled; call +workflow-enable after creation when the user wants it active.", "Before constructing steps, use +table-list and +field-list to confirm real table and field names.", "Step ids must be unique, and every next/children link must reference an existing step id.", "Use lark-base-workflow.md as the module entry and lark-base-workflow-schema.md as the steps JSON SSOT; do not invent steps[].type/data/next/children from natural language.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } pc := newParseCtx(runtime) raw, err := loadJSONInput(pc, runtime.Str("json"), "json") if err != nil { return err } if _, err := parseJSONObject(pc, raw, "json"); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { pc := newParseCtx(runtime) var body map[string]interface{} if raw, err := loadJSONInput(pc, runtime.Str("json"), "json"); err == nil { body, _ = parseJSONObject(pc, raw, "json") } return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/workflows"). Body(body). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { pc := newParseCtx(runtime) raw, err := loadJSONInput(pc, runtime.Str("json"), "json") if err != nil { return err } body, err := parseJSONObject(pc, raw, "json") if err != nil { return err } data, err := baseV3Call(runtime, "POST", baseV3Path("bases", runtime.Str("base-token"), "workflows"), nil, body, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseWorkflowDisable = common.Shortcut{ Service: "base", Command: "+workflow-disable", Description: "Disable a workflow in a base", Risk: "write", Scopes: []string{"base:workflow:update"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "workflow-id", Desc: "workflow ID (wkf... prefix)", Required: true}, }, Tips: []string{ "workflow-id must start with wkf; do not pass a tbl table ID from the same URL.", "Disable only changes workflow state; it does not delete the workflow or its steps.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } if strings.TrimSpace(runtime.Str("workflow-id")) == "" { return baseFlagErrorf("--workflow-id must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). PATCH("/open-apis/base/v3/bases/:base_token/workflows/:workflow_id/disable"). Set("base_token", runtime.Str("base-token")). Set("workflow_id", runtime.Str("workflow-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { data, err := baseV3Call(runtime, "PATCH", baseV3Path("bases", runtime.Str("base-token"), "workflows", runtime.Str("workflow-id"), "disable"), nil, map[string]interface{}{}, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseWorkflowEnable = common.Shortcut{ Service: "base", Command: "+workflow-enable", Description: "Enable a workflow in a base", Risk: "write", Scopes: []string{"base:workflow:update"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "workflow-id", Desc: "workflow ID (wkf... prefix)", Required: true}, }, Tips: []string{ "workflow-id must start with wkf; do not pass a tbl table ID from the same URL.", "Enable only changes workflow state; it does not modify steps.", "New workflows are created disabled; enable after creation only when the user wants it active.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } if strings.TrimSpace(runtime.Str("workflow-id")) == "" { return baseFlagErrorf("--workflow-id must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). PATCH("/open-apis/base/v3/bases/:base_token/workflows/:workflow_id/enable"). Set("base_token", runtime.Str("base-token")). Set("workflow_id", runtime.Str("workflow-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { data, err := baseV3Call(runtime, "PATCH", baseV3Path("bases", runtime.Str("base-token"), "workflows", runtime.Str("workflow-id"), "enable"), nil, map[string]interface{}{}, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseWorkflowGet = common.Shortcut{ Service: "base", Command: "+workflow-get", Description: "Get a single workflow definition (including steps) from a base", Risk: "read", Scopes: []string{"base:workflow:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "workflow-id", Desc: "workflow ID (wkf... prefix)", Required: true}, {Name: "user-id-type", Desc: "user ID type for creator/updater fields, default open_id", Enum: []string{"open_id", "union_id", "user_id"}}, }, Tips: []string{ "workflow-id must start with wkf; use +workflow-list if the ID is unknown.", "steps may be an empty array; that is valid for an unconfigured workflow.", "Use +workflow-get before +workflow-update, then edit the returned definition and keep fields you do not intend to change.", "Read lark-base-workflow-schema.md when interpreting or reusing returned steps.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } if strings.TrimSpace(runtime.Str("workflow-id")) == "" { return baseFlagErrorf("--workflow-id must not be blank") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { api := common.NewDryRunAPI(). GET("/open-apis/base/v3/bases/:base_token/workflows/:workflow_id"). Set("base_token", runtime.Str("base-token")). Set("workflow_id", runtime.Str("workflow-id")) if t := runtime.Str("user-id-type"); t != "" { api = api.Params(map[string]interface{}{"user_id_type": t}) } return api }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { var params map[string]interface{} if t := runtime.Str("user-id-type"); t != "" { params = map[string]interface{}{"user_id_type": t} } data, err := baseV3Call(runtime, "GET", baseV3Path("bases", runtime.Str("base-token"), "workflows", runtime.Str("workflow-id")), params, nil, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseWorkflowList = common.Shortcut{ Service: "base", Command: "+workflow-list", Description: "List all workflows in a base (auto-paginated)", Risk: "read", Scopes: []string{"base:workflow:read"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "status", Desc: "filter by status", Enum: []string{"enabled", "disabled"}}, {Name: "page-size", Type: "int", Default: "100", Desc: "page size per request, range 1-100"}, }, Tips: []string{ "Returns workflow_id values with wkf prefix; pass those IDs to +workflow-get/enable/disable/update.", "This shortcut auto-paginates and returns all matched workflows.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } _, err := common.ValidatePageSizeTyped(runtime, "page-size", 100, 1, 100) return err }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { body := map[string]interface{}{ "page_size": runtime.Int("page-size"), } if s := runtime.Str("status"); s != "" { body["status"] = s } return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/workflows/list"). Body(body). Set("base_token", runtime.Str("base-token")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { var allItems []interface{} pageToken := "" for { body := map[string]interface{}{ "page_size": runtime.Int("page-size"), } if pageToken != "" { body["page_token"] = pageToken } if s := runtime.Str("status"); s != "" { body["status"] = s } data, err := baseV3Call(runtime, "POST", baseV3Path("bases", runtime.Str("base-token"), "workflows", "list"), nil, body, ) if err != nil { return err } items, _ := data["items"].([]interface{}) allItems = append(allItems, items...) hasMore, _ := data["has_more"].(bool) if !hasMore { break } nextToken, _ := data["page_token"].(string) if nextToken == "" { break } pageToken = nextToken } runtime.Out(map[string]interface{}{ "items": allItems, "total": len(allItems), }, nil) return nil }, }
View Source
var BaseWorkflowUpdate = common.Shortcut{ Service: "base", Command: "+workflow-update", Description: "Replace a workflow's full definition (title and/or steps) in a base", Risk: "write", Scopes: []string{"base:workflow:update"}, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ {Name: "base-token", Desc: "base token", Required: true}, {Name: "workflow-id", Desc: "workflow ID (wkf... prefix)", Required: true}, {Name: "json", Desc: "workflow body JSON; read lark-base-workflow.md and lark-base-workflow-schema.md before replacing steps", Required: true}, }, Tips: []string{ "lark-cli base +workflow-update --base-token <base_token> --workflow-id <workflow_id> --json @workflow.json", "PUT uses full replacement semantics; omitting steps clears the existing workflow steps.", "Use +workflow-get first, then edit the returned definition and keep title/status/steps fields you do not intend to change.", "workflow-id must start with wkf; do not pass a tbl table ID.", "Step ids must be unique, and every next/children link must reference an existing step id.", "Updating does not enable or disable a workflow; call +workflow-enable or +workflow-disable separately.", "Use lark-base-workflow.md as the module entry and lark-base-workflow-schema.md as the steps JSON SSOT; do not invent steps[].type/data/next/children from natural language.", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if strings.TrimSpace(runtime.Str("base-token")) == "" { return baseFlagErrorf("--base-token must not be blank") } if strings.TrimSpace(runtime.Str("workflow-id")) == "" { return baseFlagErrorf("--workflow-id must not be blank") } pc := newParseCtx(runtime) if _, err := parseJSONObject(pc, runtime.Str("json"), "json"); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { pc := newParseCtx(runtime) var body map[string]interface{} body, _ = parseJSONObject(pc, runtime.Str("json"), "json") return common.NewDryRunAPI(). PUT("/open-apis/base/v3/bases/:base_token/workflows/:workflow_id"). Body(body). Set("base_token", runtime.Str("base-token")). Set("workflow_id", runtime.Str("workflow-id")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { pc := newParseCtx(runtime) body, err := parseJSONObject(pc, runtime.Str("json"), "json") if err != nil { return err } data, err := baseV3Call(runtime, "PUT", baseV3Path("bases", runtime.Str("base-token"), "workflows", runtime.Str("workflow-id")), nil, body, ) if err != nil { return err } runtime.Out(data, nil) return nil }, }
View Source
var BaseWorkspaceCreate = common.Shortcut{ Service: "base", Command: "+workspace-create", Description: "Create a workspace", Risk: "write", Scopes: []string{"base:workspace:create"}, AuthTypes: authTypes(), Flags: []common.Flag{ {Name: "name", Desc: "workspace name", Required: true}, }, Tips: []string{ `lark-cli base +workspace-create --name "Growth team"`, "Record the returned workspace_token and url; +workspace-entity-list, +workspace-move-in, and +app-create need the token.", }, DryRun: dryRunWorkspaceCreate, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeWorkspaceCreate(runtime) }, }
View Source
var BaseWorkspaceEntityList = common.Shortcut{ Service: "base", Command: "+workspace-entity-list", Description: "List bases and BaseApps in a workspace", Risk: "read", Scopes: []string{"base:workspace:read"}, AuthTypes: authTypes(), Flags: []common.Flag{ workspaceTokenFlag(true), {Name: "type", Desc: "filter by entity type: base|baseapp; omit to list both", Enum: entityTypeValues}, {Name: "page-size", Type: "int", Default: "100", Desc: "page size, range 1-100"}, {Name: "page-token", Desc: "pagination token"}, }, Tips: []string{ "lark-cli base +workspace-entity-list --workspace-token <workspace_token> --type baseapp", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if _, err := common.ValidatePageSizeTyped(runtime, "page-size", 100, 1, 100); err != nil { return err } _, err := normalizeEntityType(runtime.Str("type")) return err }, DryRun: dryRunWorkspaceEntityList, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeWorkspaceEntityList(runtime) }, }
View Source
var BaseWorkspaceMoveIn = common.Shortcut{ Service: "base", Command: "+workspace-move-in", Description: "Move an existing Base or BaseApp into a workspace", Risk: "write", Scopes: []string{"base:workspace:update"}, AuthTypes: authTypes(), Flags: []common.Flag{ workspaceTokenFlag(true), {Name: "entity-token", Desc: "base_token or app_token to move into the workspace", Required: true}, }, Tips: []string{ "lark-cli base +workspace-move-in --workspace-token <workspace_token> --entity-token <base_token>", "This moves the entity into the workspace tree; it does not create the Base or App.", "The current OpenAPI does not accept entity_type or ordering fields for move-in.", }, DryRun: dryRunWorkspaceMoveIn, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeWorkspaceMoveIn(runtime) }, }
Functions ¶
Types ¶
This section is empty.
Source Files
¶
- app_block_create.go
- app_block_get.go
- app_block_get_data.go
- app_block_list.go
- app_block_update.go
- app_create.go
- app_get.go
- app_list_block_data_config.go
- app_ops.go
- app_page_create.go
- app_page_delete.go
- app_page_get.go
- app_page_list.go
- app_page_rename.go
- base_advperm_disable.go
- base_advperm_enable.go
- base_block_create.go
- base_block_delete.go
- base_block_list.go
- base_block_move.go
- base_block_ops.go
- base_block_rename.go
- base_command_common.go
- base_copy.go
- base_create.go
- base_data_query.go
- base_errors.go
- base_form_create.go
- base_form_delete.go
- base_form_detail.go
- base_form_get.go
- base_form_list.go
- base_form_questions_create.go
- base_form_questions_delete.go
- base_form_questions_list.go
- base_form_questions_update.go
- base_form_submit.go
- base_form_update.go
- base_get.go
- base_ops.go
- base_resolve.go
- base_role_common.go
- base_role_create.go
- base_role_delete.go
- base_role_get.go
- base_role_list.go
- base_role_update.go
- base_shortcut_helpers.go
- block_data_config.go
- button_rule.go
- dashboard_arrange.go
- dashboard_block_create.go
- dashboard_block_delete.go
- dashboard_block_get.go
- dashboard_block_get_data.go
- dashboard_block_list.go
- dashboard_block_update.go
- dashboard_create.go
- dashboard_delete.go
- dashboard_get.go
- dashboard_list.go
- dashboard_ops.go
- dashboard_share.go
- dashboard_update.go
- field_create.go
- field_delete.go
- field_get.go
- field_list.go
- field_ops.go
- field_search_options.go
- field_update.go
- form_share.go
- help.go
- helpers.go
- high_risk.go
- record_batch_create.go
- record_batch_update.go
- record_delete.go
- record_export.go
- record_get.go
- record_history_list.go
- record_list.go
- record_markdown.go
- record_ops.go
- record_query.go
- record_search.go
- record_share_link_create.go
- record_upload_attachment.go
- record_upsert.go
- share_common.go
- shortcuts.go
- table_copy.go
- table_copy_ops.go
- table_copy_poll.go
- table_create.go
- table_delete.go
- table_get.go
- table_list.go
- table_ops.go
- table_update.go
- template_categories.go
- template_common.go
- template_list.go
- template_search.go
- view_create.go
- view_delete.go
- view_get.go
- view_get_card.go
- view_get_filter.go
- view_get_group.go
- view_get_sort.go
- view_get_timebar.go
- view_get_visible_fields.go
- view_list.go
- view_ops.go
- view_rename.go
- view_set_card.go
- view_set_filter.go
- view_set_group.go
- view_set_sort.go
- view_set_timebar.go
- view_set_visible_fields.go
- workflow_create.go
- workflow_disable.go
- workflow_enable.go
- workflow_get.go
- workflow_list.go
- workflow_update.go
- workspace_create.go
- workspace_entity_list.go
- workspace_move_in.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package recordexport converts the Base OpenAPI record matrix into a stable, typed row model that output formats can share.
|
Package recordexport converts the Base OpenAPI record matrix into a stable, typed row model that output formats can share. |
Click to show internal directories.
Click to hide internal directories.