mirror of
https://github.com/TencentCloud/tencentmeeting-cli.git
synced 2026-09-14 20:46:40 +08:00
93 KiB
93 KiB
Changelog
All notable changes to tmeet will be documented in this file, following the Keep a Changelog convention.
[v1.0.18] - 2026-09-11
Added
- New
eventsubcommand group — real-time event subscription via a per-host bus daemon (cmd/event/,cmd/root.go,internal/event/**): Introduces a full "per-host bus daemon + shared WSS long connection + multi-consumer fan-out" framework so agents can subscribe to Tencent Meeting real-time events (e.g.meeting.started,meeting.end) and consume them as NDJSON. Allevent consumeconsumers share a single WSS connection managed by the bus daemon, which centralises handshake / heartbeat / auto-reconnect.event list(cmd/event/list.go) — Lists subscribable EventKeys from the local built-in registry; does not require login and issues no remote calls. Optional--domainfilters by domain (e.g.meeting); unknown domain exits with code 1 and prints the set of known domains.event schema <EventKey>(cmd/event/schema.go) — Printsparams_schema/resolved_output_schema/jq_root_pathfor the given EventKey; does not require login. Unknown key exits with code 1 pointing atevent list. This is a mandatory pre-step before writing--jq/--param: a wrongjq_root_pathdoes not error, it silently drops events (e.g.meeting.started/meeting.endhavejq_root_path=.payloadandpayloaditself is an array — jq must first do.[0]before descending into fields).event consume --event-id <EventKey>(cmd/event/consume.go,cmd/event/consume_runner.go) — The actual subscription entry point; requires login (owner_hash is bound to the bus). Supports both batch mode (--max-events/--timeout) and long-running mode (neither flag, exit viaCtrl-Corevent stop). Ships with--jqclient-side projection,--param key=valuefor subscription parameters,--output-dirfor NDJSON tee-to-disk (relative paths only,..segments rejected — hard-validated), and--quietto suppress non-fatal stderr. Strict stdout / stderr separation: business events go only to stdout NDJSON; ready markers / exit summaries / diagnostics go only to stderr. Once ready, stderr emits[event] ready event_key=<key>(not muted by--quiet); on exit, stderr emits[event] exited — received <N> event(s) in <duration> (reason: limit|timeout|signal|shutdown). Does not read stdin at all: closing stdin /< /dev/null/nohup/setsidwill not trigger exit in either bounded or unbounded mode, making the command naturally compatible with agent / background scenarios. The stream does not replay historical events — only events newly produced after subscription are delivered.event status(cmd/event/status.go,cmd/event/status_stop_test.go) — Inspects the local bus daemon state; does not require login and reads only the local bus directory. Listsbuses[]withstate:running/stale_owner(credential OpenId disagrees with bus owner) /orphan(pid dead but artefacts left behind) /refused/errored. Optional--fail-on-orphanexits with code 2 when an orphan is detected, useful for CI / self-healing scripts.event stop(cmd/event/stop.go) — Stops the local bus daemon; does not require login.--forceis treated as a write operation (enters the dangerous-operation confirmation table) and is used to reaporphan/stale_ownerremnants;refused/erroredstates return exit code 2.event _bus(cmd/event/bus.go) — Hidden subcommand forked automatically byevent consume; agents must never invoke it directly. Owns the single WSS connection to the server, multiplexes subscribers, and fans events out to consumers over IPC (Unix socket / Windows named pipe).- Event framework internals (
internal/event/bus,internal/event/source,internal/event/protocol,internal/event/protocol/wsspb,internal/event/busdiscover,internal/event/spawner,internal/event/transport,internal/event/cleanup,internal/event/jqfilter): Hub / subscription registry / connection management / WSS + protobuf codec (tmeet_wss.proto) / bus discovery + PID file / cross-platform detach fork (unix / windows) / cross-platform IPC transport / session keep-alive with token refresh / event de-duplication / jq projection engine with unit tests. - New exit-code contract:
0(normal) /1(fatal: Hello rejected, unknown EventKey, IO error, subscription failure, ...) /2(only forevent status --fail-on-orphanandevent stoponrefused/errored). - New SKILL section and reference doc (
skills/tmeet-skill/references/tmeet-event.md, 483 lines): full coverage of use-case routing, mandatory call sequence, subprocess contract (ready / exited markers, exit codes, stdin EOF behaviour, stderr diagnostics), red lines (schema-before-jq, nokill -9, multi-account switching, path validation, privacy in output,_busnever called directly), bus-remnant self-heal flow, EventKey field reference, and a common-failure lookup table.
- Event-related client error codes (
internal/exception/client_code.go,internal/exception/errors.go):ClientCodeEventInternal = 4000/ClientCodeEventBus = 4001/ClientCodeEventBusNotRunning = 4002, along with matchingEventInternalError/EventBusError/EventBusNotRunningErrorconstants. - WSS token-expiry server codes (
internal/exception/server_code.go):ServerCodeWssTokenExpired = 200010203/ServerCodeWssHeadTokenExpired = 10006, consumed by the WSS long-connection side to detect expiry and trigger token refresh. WSSendpoint constant (internal/core/endpoints.go):Endpoints.WSSandGetWSSEndpoint(), pointing tomeeting.tencent.com, used by the event framework to open the WSS long connection.config.ResourceReleaseHookteardown-hook mechanism (internal/config/user.go,internal/config/resource_hooks_test.go): NewRegisterResourceReleaseHook/ResetResourceReleaseHooksForTestand aClearUserConfigFunctype. Credential teardown (ClearUserConfig) now runs every registered hook first in registration order (fail-fast: any hook error or panic aborts teardown and leaves keychain +active_open_idintact so the user can retry), and only proceeds to keychain / meta cleanup if all hooks succeeded.cmd/root.goregisters anevent-bushook viaregisterResourceReleaseHook()(dispatching tointernal/event/cleanup.OnUserCleared) soauth logoutand the refresh-token failure fallback gracefully stop the outgoing account's bus, preventing residue from leaking into the next login session. Deliberately placed ininternal/config(the lowest package in the dependency graph) to avoid aninternal/event→internal/configcycle.- Process-lifetime exclusive lock
filelock.ProcessLock(internal/core/filelock/processlock.go,internal/core/filelock/filelock.go,internal/core/filelock/filelock_unix.go,internal/core/filelock/filelock_windows.go): Distinct from the existing short-livedWithLock(poll + defer auto-release),ProcessLockacquires non-blockingly and holds for the rest of the process; the OS releases it at process exit. Foundation for the bus daemon "am I alive?" probe (the bus takes aProcessLockonbus.alive.lockat startup and never releases it; any subsequentTryLocksucceeding means the previous holder died). The cross-platform lock primitiveslockFDNonBlocking/unlockFDare extracted so bothWithLockandProcessLockshare a single well-tested implementation. ExportsErrHeldforerrors.Isto distinguish contention from real syscall failures. log.InitNamedand instance-levelLoggerhelpers (internal/log/logging.go): NewInitNamed(logDir, subDir, prefix, level)lets long-running auxiliary processes (notably the event bus daemon) own their own log namespace while still benefiting from rotation / retention / async writes. The returned*Loggeris not registered as the globaldefaultLogger; callers own it and must callClose(). Added instance-level(*Logger).Debugf/Infof/Warnf/Errorfand a(*Logger).Close()that is idempotent (double-close does not panic).thttp.DefaultNoProxyHttpClientdirect-connection fallback client and per-requestWithRequestClientoption (internal/core/thttp/client.go,internal/core/thttp/request.go): New*http.ClientwithProxy: nil(all other transport tunables matchDefaultHttpClient) for use as a retry path when a CGI request fails at the network layer;WithRequestClient(clt)is a new per-request option that overrides the client-level*http.Clientfor a single request without mutating the shared client.common.BuildUniqueID(openID, machineID)unifiesTmeet-Unique-ID/cli_uniq_idformatting (internal/common/system.go): The"<openId>*<machineId>"concatenation is centralised in one helper. The REST proxy'sTmeet-Unique-IDrequest header and the WSSAuthBindReq.cli_uniq_idfield now share the same implementation, ruling out format drift between the two paths.build.shnow runs unit tests before compilation (build.sh): Runsgo test -count=1 ./...up front and aborts on failure (via the script'sset -e), baking "test before build" into the release flow.
Changed
ClearUserConfigis now a two-phase "release resources, then clear credentials" flow (internal/config/user.go,internal/auth/auth.go,internal/proxy/rest-proxy/proxy.go): The newClearUserConfigruns (1) readActiveOpenId; (2) dispatch every registeredResourceReleaseHookfail-fast — any failure or panic leaves keychain +active_open_idintact so the user can retry; (3) remove the keychain entry; (4) clearactive_open_idfrom meta. The former implementation is preserved asClearUserConfigUnResource.auth.RefreshTokennow takes aClearUserConfigFuncargument (wired toconfig.ClearUserConfigat the call site) so it no longer hard-depends on a specific teardown function, and so event / test paths can inject different teardown strategies.- Record-state enum descriptions refined (
internal/utils/enumerate/record_state.go,internal/utils/enumerate/record_state_test.go,internal/utils/converter_test.go):录制中→录制中,不可查看或申请,转码中→转码中,不可查看或申请,转码完成→转码完成,可根据录制文件权限进行下一步; unit tests updated in lock-step. log.Init/log.Closeinternals refactored aroundnewLoggerand instance-levelClose(internal/log/logging.go): Package-levelInitnow delegates to the sharednewLoggerconstructor, and package-levelClosedelegates todefaultLogger.Close().LoggergainssubDir/prefixfields withfilePrefix()/fileSubDir()fallbacks so path composition, rotation and cleanup all route through instance methods instead of the hard-coded package-levellogPrefix/logSubDir, unlocking the standalone log namespace used by the event bus daemon.minutepackage and CLI command renamed tominutes(cmd/minute/→cmd/minutes/,cmd/root.go): CLI entry point changes fromtmeet minute search|gettotmeet minutes search|get;skills/tmeet-skill/references/tmeet-minute.mdrenamed totmeet-minutes.md; everyminutereference in SKILL and README (command tree, routing decision tables, error hints) rewritten tominutes.cmd/root.gowires theeventsubcommand group and enables the resource-release hook (cmd/root.go): AddsrootCmd.AddCommand(event.NewBaseCmd(tmeet)); callsregisterResourceReleaseHook()at the top ofExecute()(deliberately not ininit()— registering ininit()would activate the hook whenever thecmd/rootpackage is imported, e.g. by tests, and risk accidentally tearing down the user's bus); also drops a redundantfmt.Sprintfwrapper aroundoutput.PrintErrorfin favour of the native printf-style signature.- README trimmed of the long command-reference section (
README.md,README_EN.md,docs/command.md,docs/command_en.md): The ~1000-line "Command reference" section is moved out of README into the newdocs/command.md/docs/command_en.md. README keeps only the command tree plus the overview of pagination /--compact/--format. The command tree gainsevent list|schema|consume|status|stop, and the pagination table renamesminute search|gettominutes search|get. - SKILL bumped to 1.0.18, fully embracing the event group and the minutes rename (
skills/tmeet-skill/SKILL.md,skills/tmeet-skill/references/tmeet-event.md,skills/tmeet-skill/references/tmeet-minutes.md):- Top-level description extended: adds "实时事件订阅(会议/录制/纪要等事件流)" to the capability description.
- Login-prerequisite exceptions extended: on top of
auth login/auth status,event list/event schema/event status/event stopare also login-free so users can inspect bus remnants from an unauthenticated state. - Command tree gains an
eventbranch, linked to the newreferences/tmeet-event.md. --formatsection gains an exception clause: theeventsubcommand family emits bare JSON — no{trace_id, message, data}envelope (e.g.event consumeemits NDJSON lines shaped{event, trace_id, payload};event listemits[{...}, ...]directly) — and does not go through the compact middleware, so--compacthas no effect on event output. Useevent consume --jqfor projection instead.- Yuanbao-minutes routing and the "minutes vs recording" decision table: every
minute get|searchreference rewritten tominutes get|search. - Common-errors table gains an
event consumefailure entry: missing ready marker / exit code 1 /event stopreturningrefused, etc., all delegate totmeet-event.md's common-failure lookup table.
Fixed
Base64DecodeConverterno longer leavesRawStdEncoding/URLEncodingfields as raw ciphertext (internal/utils/converter.go): Previously the converter only fell back toRawURLEncodingonce whenStdEncodingdecoding failed — coverage was incomplete: if the server returnedRawStdEncoding(standard encoding without padding) orURLEncoding(URL-safe encoding with padding), decoding failed and the raw Base64 ciphertext was returned to the caller, surfacing as "an unreadable blob" in the model / user output. The converter now walksStdEncoding→RawStdEncoding→URLEncoding→RawURLEncodingin order, validatingutf8.Validon each success, and returns the decoded plaintext at the first hit; only if all four encodings fail does the original value pass through untouched.- CGI requests would fail wholesale when a stale proxy env var was inherited (
internal/proxy/cgi-proxy/proxy.go,internal/core/thttp/client.go,internal/core/thttp/request.go): Agent-sandbox scenarios often setHTTP_PROXY/HTTPS_PROXY/ALL_PROXYpointing at a sidecar proxy, and a long-lived (or sandbox-spawned)tmeetprocess inherits those env vars. If the sidecar is torn down but the env vars remain,DefaultHttpClientdials a dead proxy on every request and fails at the network layer — the user-visible symptom is "every CGI-backed command times out / connection refused". Fix: introduceDefaultNoProxyHttpClient(Proxy: nildirect-connection client) plus a per-requestWithRequestClientoption. WhenRequestProxydetects a network-layer failure (not a non-200 HTTP response — an actualc.clt.Doerror, meaning the request never got a response), it retries once over the direct-connection client using a brand-new Request struct so the retry does not inherit the authenticators / headers already appended by the first attempt; non-200 statuses are not retried (the server did respond, so retransmission would not help). The whole path also gains structuredlog.Errorf/log.Warnfdiagnostics, with sensitive headers (Set-Cookie/Authorization/Cookie) redacted viaredactHeaderbefore being logged.
[v1.0.17] - 2026-09-09
Added
- New
appsubcommand group (cmd/app/,cmd/root.go): Manages the current logged-in user's own CLI app (cli-app) presentation in Tencent Meeting — app display name, homepage URL, and in-meeting open layout. Scope is strictly limited to the current user; it does not affect other participants or the meeting itself. Wired throughWithApiCmd(ApiCmdAppInfoGet / ApiCmdAppInfoSet)and the middleware pipeline.app get(cmd/app/get.go) — Retrieves the current CLI app configuration. Takes no parameters; backed byGET /v1/cli/app-detail.app set(cmd/app/set.go) — Updates the CLI app configuration viaPUT /v1/cli/app-info. At least one of the three flags is required (MarkFlagsOneRequired); fields not provided are left untouched on the server side.--homepage— App homepage URL. Non-empty: the CLI app is pushed to the meeting client and displayed in-meeting; empty string: the CLI app is no longer pushed in-meeting (this is effectively the "show in meeting" master switch). When non-empty, the URL is strictly validated by the newutils.ValidateURL(must start withhttp://orhttps://, host non-empty) and length-capped to ≤ 200 characters viautils.CharacterLimit.--layout-style— In-meeting open layout, validated client-side via thecmdutil.EnumValuetype introduced in v1.0.15. Accepts onlysidebar(narrow sidebar, 375px wide) /wide_sidebar(wide sidebar, 735px wide) /popout(standalone popout, 960×540px). When omitted, the CLI does not send the field: server default becomessidebarif unset, existing value is preserved otherwise.--sdk-name— In-meeting display name for the app. Length-capped at 20 in display width via the newutils.CharacterWidthLimit(ASCII counts as 1, non-ASCII such as Chinese counts as 2); also rejects pure-whitespace and empty strings.
- New
utils.ValidateURLURL validation helper (internal/utils/validate.go,internal/utils/validate_test.go): Built onnet/url.Parse. Requires scheme to behttporhttps(case-insensitive), host to be non-empty, and rejects the empty string outright. Unit tests cover http/https, missing scheme,ftp://, empty host, and empty input. Currently consumed byapp set's--homepage. - New
utils.CharacterWidthLimitdisplay-width limiter (internal/utils/string.go): Complements the v1.0.6CharacterLimit(which counts UTF-8 runes) by counting display width — ASCII as 1, non-ASCII as 2. On overflow it returns anInvalidArgsErrorcarrying the flag name, the configured limit, and the actual width. Currently consumed byapp set's--sdk-name. - New ApiCmd constants (
internal/cmdutil/api_schema.go):ApiCmdAppInfoSet/ApiCmdAppInfoGet— so the newappcommand group plugs into--compactand the middleware pipeline.
Changed
record listresponse now carries an extrastate_intfield (cmd/record/list.go): Before the rawstateis transformed into a human-readable Chinese description byRecordStateConverter, the raw integer state code is recursively copied into a siblingstate_intfield via the newcopyStateRaw/copyStateIntRecursivehelpers.state_intis automatically appended to the--compactwhitelist so it is preserved even in compact mode, letting callers programmatically branch on record state while keeping the human-readable description intact.meeting getrecord enrichment now scopes recording queries by the meeting's time window (cmd/meeting/record_enrich.go):enrichMeetingWithFullRecordsnow reads each meeting object'sstart_time/end_time, parses them viaparseUnixSeconds, and forwards them asstart_time/end_timequery params to the paginated recording-info API. This explicitly constrains the returned records to that meeting's time window and prevents cross-meeting historical records from bleeding into the enrichment.- SKILL bumped to 1.0.17 with the new
appcommand group and related rules (skills/tmeet-skill/SKILL.md,skills/tmeet-skill/references/tmeet-app.md,skills/tmeet-skill/references/tmeet-meeting.md):- Top-level description extended: Adds "CLI 应用信息管理(设置/查询自己的 CLI 应用在会中的展示配置)" to the capability description.
- Command tree extended with
appgroup, linked to the newreferences/tmeet-app.md. - New
references/tmeet-app.mdreference doc: Fully covers the use-case table,get/setparameter tables, invocation rules (--homepageon/off semantics, URL legality, thehttp://vshttps://protocol-selection decision table,https://certificate-trust requirement, "--layout-stylevalues must never be inferred"), and the post-success announcement rules (with heavy emphasis on the fact thatsetchanges do not take effect in the currently-joined meeting — the user must rejoin to see the change, preventing agents from retryingsetin a loop inside the same meeting). http://vshttps://client-compatibility decision table: Tencent Meeting client versions prior to 3.45.10 do not support openinghttp://pages. For anyhttp://URL provided by the user, the agent must first confirm the client version before invokingapp set, and must not skip the confirmation just because the user verbally claims to be on a "new version".- New error code
500294handling rule (SKILL.mdcommon-errors table +tmeet-app.md): When the current account's meeting plan does not entitle this feature, the backend returns an error description containing an upgrade link. The agent must return the URL verbatim and render it as a clickable Markdown link[升级会议套餐](URL)— no rewriting, no parameter dropping, no wrapping in backticks or code blocks, and no self-invented plan-type reasoning. - New error code
500284handling rule (SKILL.mdcommon-errors table): "该功能暂不可使用" — the current account has no entitlement (possibly ungrayed feature, insufficient commercial rights, etc.). Retries are strictly forbidden — the agent must inform the user truthfully that the feature is unavailable on the current account and that interpretation authority belongs to Tencent Meeting; it must not speculate on the cause or promise activation paths. meeting update--meeting-type 1usage clarified (references/tmeet-meeting.md): Corrects the over-strict wording from v1.0.16 ("modifying a recurring meeting requires--meeting-type 1"). The refined rule:--meeting-type 1is required only when modifying recurrence rules (--recurring-type/--until-type/--until-count/--until-date) or modifying a specific sub-meeting (--sub-meeting-id). When only editing subject, time, password, join restrictions, watermark, recording, or invitees — i.e. non-recurrence fields —--meeting-typeis not required; the server will not silently downgrade a recurring meeting to a regular one, and the recurrence rule is preserved.
- README sync (
README.md/README_EN.md): Command tree extended withapp get/app set; newappsection with parameter table and examples (client 3.45.10 compatibility notice,https://certificate-trust requirement,--sdk-namedisplay-width cap, and the three--layout-styleoptions).
Fixed
meeting create/meeting updategating logic for--join-typeand--waiting-roomcorrected (cmd/meeting/create.go,cmd/meeting/update.go): Previously--join-typewas gated by> 0and--waiting-roomwas gated by the bool value, which caused two bugs: (1) explicitly passing--join-type 0was silently dropped and never sent to the server; (2)updatecould not turnwaiting-roomoff once it had been turned on — the bool zero valuefalsewas indistinguishable from "flag not provided" and was skipped. Both flags now usecmd.Flags().Changed(...)— consistent with other advanced-setting fields (--water-mark-type, etc.): if the user did not explicitly pass the flag, nothing is sent and the server value is preserved; only explicit input is written intosettings. Users can now reliably switch join-type back to "all members" and turn off an already-enabled waiting room.
[v1.0.16] - 2026-09-02
Added
- New
minutesubcommand group (cmd/minute/): Yuanbao meeting-minutes capabilities — search across meetings by keyword / time range, and retrieve full minute detail (steady-state or rolling transient) by minute ID / meeting ID / meeting code.minute search(cmd/minute/search.go) — Search Yuanbao minutes by keyword and/or time range. All filter parameters are optional and freely combinable. Backed byGET /v1/mcp/asr/search-minutes-mcpand wired throughWithApiCmd(ApiCmdMinuteSearch).--query— Search keyword; bounded bymaxQueryLen = 50runes and validated viautils.CharacterLimit.--start/--end— ISO 8601 window bounds.--start >= --endis rejected client-side withInvalidArgsError.--page-token/--page-size(default20, max50, capped viacmdutil.ClampingPageSize+pageSizeMaxMinutes) — Standard pagination.- Response is post-processed with
WithTotalCountLogic.
minute get(cmd/minute/get.go) — Query Yuanbao minute detail by minute ID, meeting ID, or meeting code. Supports two modes via the--short-summaryflag, resolved at runtime byFlagSwitchWithDefaultinto eitherApiCmdMinuteGet(stable) orApiCmdMinuteGetTransient(transient).- Stable mode (default): Fetches steady-state minutes via
GET /v1/mcp/asr/get-minutes-mcp. Accepts--minute-id,--meeting-id, or--meeting-code(three-way choice);--meeting-codeis automatically resolved tomeeting_idviaGET /v1/meetings. Supports--sub-meeting-idfor recurring meetings,--overview/--summary-points/--todoscontent toggles (all defaulttrue), and--page-token/--page-size(default10, max30) pagination. - Transient mode (
--short-summary): Fetches rolling (transient) minutes viaGET /v1/mcp/asr/get-transient-minutes-mcp. Requires--minute-id. Automatically loops through all pages (up tomaxPages = 100) usingnext_page_token, merging allitemsinto a single response withhas_more: false. Page size defaults to100, max300.
- Stable mode (default): Fetches steady-state minutes via
- Meeting list enriched with Yuanbao minute summary (
cmd/meeting/minute_enrich.go):meeting get,meeting list,meeting list-ended, andmeeting searchnow automatically enrich each meeting object withminute_total_countand aminutesarray (minute ID, start time, overview) by callingPOST /v1/mcp/asr/get-meeting-minutes-summary-mcpafter the meeting query completes. The enrichment is best-effort — on any failure the original data is returned unchanged so meeting output is never blocked by minute query errors.enrichMeetingsWithMinutes()collectsmeeting_id(and optionallysub_meeting_id) from the meeting list, batch-queries the minutes summary API, and merges results back into each meeting object.normalizeMinutes()convertsminute_start_timefrom unix timestamp to human-readableYYYY-MM-DD HH:MMformat in-place, handling both second-level and millisecond-level timestamps.- Under
--compact, the client-side injectedminutes/minute_total_countfields are preserved via the newcompactFieldsWithAllEnrichments()(cmd/meeting/enrich.go), which merges bothrecordEnrichmentFieldsandminuteEnrichmentFieldsinto the remote schema whitelist.
- New
agent_init.pyscript for agent identity initialization (skills/tmeet-skill/scripts/agent_init.py): A standalone Python script that atomically writesagent.json(AI-Agent name + LLM model) to the tmeet config directory. Resolves config dir fromTMEET_CLI_CONFIG_DIRenv var with~/.tmeet/fallback. Uses tmp-file +os.replacefor atomic writes, mirrors the Go implementation's behavior. Replaces the previousTMEET_AGENT/TMEET_MODELenvironment-variable approach that was wired intoauth login. - New ApiCmd constants (
internal/cmdutil/api_schema.go):ApiCmdMinuteSearch,ApiCmdMinuteGet,ApiCmdMinuteGetTransient— so the new minute commands plug into the--compactand middleware pipeline.
Changed
- Agent identity initialization decoupled from
auth login(cmd/auth/login.go): TheTMEET_AGENT/TMEET_MODELenvironment-variable reading andconfig.SaveAgentConfig()call have been removed from the login flow. Agent identity is now managed externally viascripts/agent_init.py, which the SKILL instructs the model to run silently at the start of each new session or on model switch. This decoupling means agent identity can be updated without re-login, and login no longer has a side-effect dependency on environment variables. compactFieldsWithRecordsreplaced bycompactFieldsWithAllEnrichments(cmd/meeting/enrich.go,cmd/meeting/record_enrich.go): The per-enrichmentcompactFieldsWithRecords()helper has been removed fromrecord_enrich.goand replaced by a unifiedcompactFieldsWithAllEnrichments()in the newenrich.go, which merges bothrecordEnrichmentFieldsandminuteEnrichmentFieldsinto the compact whitelist.meeting list,meeting list-ended, andmeeting searchnow callcompactFieldsWithAllEnrichments()instead ofcompactFieldsWithRecords().package.jsonversion bumped tov1.0.15(package.json): npm package version updated fromv1.0.14tov1.0.15.- SKILL bumped to 1.0.16 with Yuanbao minutes routing and agent-init overhaul (
skills/tmeet-skill/SKILL.md,skills/tmeet-skill/references/tmeet-minute.md,skills/tmeet-skill/references/tmeet-auth.md):- New
minutecommand group in command tree:minute searchandminute getadded to the command overview with a link to the newreferences/tmeet-minute.md. - New "元宝纪要查询" routing section in SKILL.md: A comprehensive decision table distinguishing "known meeting" (permission-based routing:
can_view→record smart-minutes, otherwise →minute get) from "cross-meeting content search" (dual-path:minute search+record search --query-field transcript_content). Includes explicit rules for dual-search fallback, source labeling, and terminology discipline ("元宝纪要" vs "智能纪要"). - New "纪要 vs 录制" disambiguation table: Inserted above the existing "查询命令选择准则" to route "纪要/总结/要点/待办" requests to the correct pipeline before the user's query is misclassified as a meeting-metadata query.
- New
references/tmeet-minute.md: Full reference doc for theminutecommand group — routing guidelines,search/getparameter tables, response field tables, stable vs transient mode documentation, typical workflows, and terminology rules. - Agent-init guidance rewritten: The
TMEET_AGENT/TMEET_MODELenvironment-variable instruction replaced with apython3 ./scripts/agent_init.py --agent ... --model ...invocation rule — must run silently at session start or model switch, failure is ignored, same combination is not repeated. tmeet-auth.mdupdated: Agent identity note changed from env-var approach toagent_init.pyscript invocation.
- New
- README sync (
README.md/README_EN.md): Command tree updated withminute search/minute get; pagination quick-reference table extended withminute search(default 20, max 50) andminute get(default 10, max 30); full parameter tables and usage examples added for both commands in both Chinese and English READMEs.
[v1.0.15] - 2026-08-07
Added
- New
control waiting-roomsubcommand (cmd/control/waiting_room.go,internal/cmdutil/api_schema.go,internal/utils/enumerate/waiting_room_operate_type.go): Manage waiting room members in an ongoing meeting. Backed byPUT /v1/real-control/meetings/{meeting_id}/waiting-roomand wired throughWithApiCmd(ApiCmdControlWaitingRoom). Classified as a write operation and listed in the SKILL dangerous-operations table.--meeting-id(required) — Meeting ID.--operate-type(required) — One ofenter-meeting(admit into meeting) /back-to-waiting(move back to waiting room) /expel(expel from waiting room). Uses the new reusablecmdutil.EnumValuecobra flag type for client-side validation.--allow-rejoin(optional,--operate-type=expelonly) — Whether expelled members are allowed to rejoin. Only sent to the server when the flag is explicitly changed by the user.--users/--sip-users/--pstn-users(at least one required) — Map respectively to regular members'open_id, SIP devices'ms_open_id(instanceid=9), and PSTN devices'ms_open_id(instanceid=0). Total across all three capped atMeetingControlUsersListMax = 20, reusingPackageMeetingControlUsers/PackageMeetingControlSpecialUsers. The kick-target source hard rule from v1.0.8 applies here as well: user identifiers must come fromreport participants.--operate-typeis converted from the CLI string to the downstream API'suint32enum via the newWaitingRoomOperateTypeValuelookup, backed by theWaitingRoomOperateTypeenum and its bidirectional name/value map.
- New
report participants-exportsubcommand (cmd/report/participants_export.go,internal/cmdutil/api_schema.go): Export meeting participants list as an async task. Backed byPOST /v1/meetings/export-participants-listand wired throughWithApiCmd(ApiCmdReportParticipantsExport).--meeting-id(required) — Meeting ID.--sub-meeting-id(optional) — Sub-meeting ID for recurring meetings.--start/--end(optional) — ISO 8601 window bounds, each converted to a unix timestamp viautils.ISO8601ToTimeStampbefore being sent to the server.--file-type(defaultxlsx) — Export format:xlsxorjson; validated client-side.
- New
report job-resultsubcommand (cmd/report/job_result.go,internal/cmdutil/api_schema.go,internal/utils/enumerate/export_job_status.go): Query the result of an async export task. Backed byGET /v1/export/{job_id}and wired throughWithApiCmd(ApiCmdReportJobResult).--job-id(required) — Task ID returned byreport participants-export.- The response
statusfield is post-processed with the newExportJobStatusConverter, mapping integer status codes (1=成功/2=失败/3=处理中) to Chinese display names via theExportJobStatusint enum.
- Meeting list enriched with recording basic info (
cmd/meeting/record_enrich.go):meeting get,meeting list,meeting list-ended, andmeeting searchnow automatically enrich each meeting object withrecords_total_countand arecordsarray (recording subject, duration, start time, state, type) by calling the recording APIs after the meeting query completes. The enrichment is best-effort — on any failure the original data is returned unchanged so meeting output is never blocked.meeting getuses the paginatedGET /v1/mcp/records/meet-basic-info-listAPI (capped atmaxFullRecordPages = 2×fullRecordPageSize = 100= 200 records max) to fetch the full recording snapshot.meeting list/meeting list-ended/meeting searchuse the batchedPOST /v1/mcp/records/meet-basic-infoAPI for a lightweight preview.normalizeRecords()inlinesstate_int/type_int→ human-readablestate/typestring renaming and drops the_intfields, so the output never leaks raw integer codes.- Under
--compact, the client-side injectedrecords/records_total_countfields are preserved viacompactFieldsWithRecords(), which mergesrecordEnrichmentFieldsinto the remote schema whitelist — the remote schema does not know about these fields since they are injected client-side.
- New reusable
EnumValuecobra flag type (internal/cmdutil/enum.go): Apflag.Valueimplementation that validates the flag input against a predefined allowlist at parse time, returning a clearInvalidArgsErrorlisting the allowed values when the input is unrecognized. Used bycontrol waiting-room's--operate-typeand available for future enum-constrained flags. - New
DurationSecondsConverter(internal/utils/converter.go): Seconds-based counterpart of the existingHHMMSSConverter(which expects milliseconds). Convertsfloat64orstringsecond values toHH:MM:SS/MM:SSformat. Applied to thedurationfield in recording objects enriched into meeting output. - New
ExportJobStatusint enum andExportJobStatusConverter(internal/utils/enumerate/export_job_status.go,internal/utils/converter.go): Maps export job status IDs to Chinese display names; wired intoreport job-resultoutput at pathstatus. - New
WaitingRoomOperateTypeenum (internal/utils/enumerate/waiting_room_operate_type.go): Defines the three waiting room operations (enter-meeting=1/back-to-waiting=2/expel=3) with a bidirectional name↔value map;WaitingRoomOperateTypeNamefor display andWaitingRoomOperateTypeValuefor CLI-to-API conversion. - New ApiCmd constants (
internal/cmdutil/api_schema.go):ApiCmdReportParticipantsExport,ApiCmdReportJobResult,ApiCmdControlWaitingRoom— so the new commands plug into the--compactand middleware pipeline.
Changed
- Windows DPAPI keychain now self-heals on unrecoverable master-key failure (
internal/core/keychain/keychain_windows.go): When the DPAPI master key becomes permanently unusable (e.g. Windows password force-reset by an administrator, account type switched between local/Microsoft account, Windows Hello/PIN rebuilt, system restored to a pre-credential state), the keychain now detects the condition via the newisDPAPIKeyInvalid()helper — classifyingNTE_BAD_KEY_STATE,ERROR_INVALID_DATA, andNTE_BAD_DATAas "permanently dead" — and auto-purges every registry value under the keychain path viapurgeStaleRegistryEntries(), then returnsErrNotFound. The upper layer transparently generates a fresh master key on the next login, so the user is simply prompted to re-login instead of being permanently stuck on a "Key not valid for use in specified state" error. Only unambiguous "key permanently unusable" errors trigger self-healing; transient failures (permission denied, EDR interception, DLL not loaded) are surfaced as-is and do not wipe recoverable ciphertext. - Windows keychain
Removenow cascade-deletes master key on last logout (internal/core/keychain/keychain_windows.go): When removing the last remaining credential (onlymaster_keyleft in the registry), the master key is now automatically deleted as well. This prevents a leftover undecryptable zombie master key from permanently blocking the next login after a future DPAPI credential change. Regenerating a master key on the next login is cheap (<10ms) and avoids a hard-to-diagnose failure mode. Multi-account scenarios (other business ciphertexts still present) are unaffected — master key is only deleted when it is the sole remaining value. Base64DecodeConverterhardened against non-Base64 and non-UTF-8 input (internal/utils/converter.go): The converter now validates that the decoded output is valid UTF-8 before returning it (prevents garbled output when a field happens to look like Base64 by coincidence). The fallback path switched fromURLEncoding(with padding) toRawURLEncoding(no padding), matching the encoding used by some backends. Empty string inputs now short-circuit without a decode attempt.- Record state enum descriptions now carry actionable hints (
internal/utils/enumerate/record_state.go):RecordStateRecording→录制中,不可查看或申请,RecordStateTranscoding→转码中,不可查看或申请,RecordStateDone→转码完成,可根据录制文件权限进行下一步. These descriptions are surfaced in the meeting recording enrichment output so the user immediately knows whether a recording is actionable. - Build script now hardens binary permissions with self-check (
build.sh): After compilation, all non-.exebinaries are automaticallychmod-ed to0755, and a mandatory-xcheck follows — if any binary lacks the executable bit, the build fails with a clear error. This prevents broken releases caused by umask anomalies, post-processing steps, or publishing environments that strip thexbit. - npm wrapper
tmeet.jsnow uses a three-layer executable fallback (scripts/tmeet.js): (1)accessSync(X_OK)probe — if the binary is already executable, use it directly (fast path for publish artifacts with baked-in0755); (2) in-placechmodSync— for conventional environments that permitchmod; (3) copy toos.tmpdir()+chmod— for sandbox/read-only-FS environments where the original path rejectschmodwithEROFS/EPERM/EACCES/ENOSYS. A simplesize+mtimecache avoids redundant IO on every cold start. WithCompactdocumentation improved (internal/output/options.go): Now clearly documents that the compact field list is used as-is and that callers with client-side injected fields (e.g.recordsenrichment) must merge them into the slice beforehand rather than relying on the option to do it.- SKILL bumped to 1.0.15 with major structural overhaul (
skills/tmeet-skill/SKILL.md,skills/tmeet-skill/references/tmeet-meeting.md,skills/tmeet-skill/references/tmeet-record.md,skills/tmeet-skill/references/tmeet-report.md,skills/tmeet-skill/references/tmeet-control.md,skills/tmeet-skill/references/tmeet-contact.md,skills/tmeet-skill/references/tmeet-tshoot.md):- New "命令总览与详情索引" section: A full command tree with clickable Markdown links to every reference doc, replacing the scattered per-module descriptions.
- New "安全规则" centralized section: Gathers all safety rules from scattered locations into one chapter — dangerous-operation confirmation table (with explicit "wait is hard requirement" anti-pattern table covering self-Q&A, fabricated user instructions, and default-option auto-selection), member-id privacy rule (
meeting_idmust not be exposed to users), contact-usage gate, kick-target source constraint, multi-result confirmation rule, and missing-parameter prohibition. - New "参数规范" section: Consolidates time format,
--format,--compact, and pagination rules under a single heading. - "查询命令选择准则" relocated and expanded: Now directly below the command tree with an explicit "list vs search" decision table, a recording-query routing pointer to
tmeet-record.md, and anti-pattern rules (no keyword-cramming intolist, no keyword-as-time confusion, clarify ambiguous input). - "核心概念" section removed: The concepts are now covered inline in the respective reference docs.
tmeet-report.mdupdated: Added parameter tables and usage notes forparticipants-exportandjob-result, documenting the export→poll-result async workflow.tmeet-control.mdupdated: Addedwaiting-roomcommand with parameter table and the three operation-type scenarios.tmeet-meeting.mdupdated: Documented the 90-day query interval limit formeeting search's--start/--end(server returns190004when exceeded; caller must split into ≤90-day windows). Recording enrichment fields (records,records_total_count) documented in meeting output schemas.tmeet-record.mdupdated: Added "录制查询路由总则" defining the multi-tier routing rules acrossmeeting get/meeting search/meeting list-ended/record list/record search/record transcript-search, pluspermission_statuspermission-judgment guidance.tmeet-contact.mdandtmeet-tshoot.md: Refined error-handling guidelines and simplified document structure.
Fixed
meeting getrecording duration field now usesDurationSecondsConverter(cmd/meeting/get.go): Previously the recordingdurationfield (expressed in seconds by the backend) was mistakenly routed throughHHMMSSConverter(which expects milliseconds), producing durations that were 1000× too small. The newDurationSecondsConvertercorrectly interprets the value as seconds and produces the canonicalHH:MM:SS/MM:SSoutput.
[v1.0.14] - 2026-07-28
Added
- New
meeting searchsubcommand (cmd/meeting/search.go,internal/cmdutil/api_schema.go): Keyword / meeting-code / time-window search for meetings, complementing the pure time-windowmeeting listandmeeting list-ended. Backed by the newGET /v1/meetings/search-meetingsendpoint and wired through the standard middleware chain (WithApiCmd(ApiCmdMeetingSearch)+WithCompact). All filter flags are optional and freely combinable.--query— Search keyword; bounded bymaxQueryLen = 50runes and validated viautils.CharacterLimit.--query-field(defaultall) — Field to match--queryagainst:subject/creator(creator nickname or remark name) /note(user's private note on the meeting) /all.--meeting-code— Exact-match meeting code; normalized via the newcmdutil.FormatMeetingCodebefore being sent to the server.--start/--end— ISO 8601 window bounds. The server matches a meeting if any of its scheduled start, actual start, or the current user's join time falls inside the window.--start >= --endis rejected client-side withInvalidArgsError.--page-token/--page-size(default30, max30, capped viacmdutil.ClampingPageSize+cmdutil.PageSizeMaxMeetings) — Standard v1.0.5 pagination.- Response is post-processed with
MeetingTypeConverter+ the newMeetingStatusInSearchConverteratmeetings.status, thenWithCompactandWithTotalCountLogic(see below).
- New
record searchsubcommand (cmd/record/search.go,internal/cmdutil/api_schema.go): Keyword / meeting-code / meeting-id / time-window / file-type search across recordings and their AI-derived artifacts (transcript / smart minutes / timeline). Backed byGET /v1/mcp/records/searchand wired throughWithApiCmd(ApiCmdRecordSearch)+WithCompact.--query(bounded bymaxQueryLen = 50runes, validated viautils.CharacterLimit) and--query-field(defaultall) — Field set issubject/creator/transcript_content/smart_minutes/timeline/all; the last three let the user grep the recording's derived content, not just its metadata.--file-type(defaultall) — Restrict results tovideo/audio/transcript/upload/external/all.--meeting-id/--meeting-code— Optional filters;--meeting-codeis normalized viacmdutil.FormatMeetingCode.--start/--end— ISO 8601 window bounds with the samestart >= endclient-side guard.--page-token/--page-size(default30, max30, capped viacmdutil.PageSizeMaxRecords).
- New
MeetingStatusInSearchstring enum and converter (internal/utils/enumerate/meeting_status_in_search.go,internal/utils/enumerate/meeting_status_in_search_test.go,internal/utils/converter.go): Maps the search endpoint'sstatusvaluesunknown/upcoming/in_progress/closedto their Chinese display names (未知状态/待开始/进行中/已结束). Unrecognized inputs fall back toUnknown. Wired intomeeting searchoutput at pathmeetings.statusvia the newMeetingStatusInSearchConverter(stringEnumConverter-backed). - New
output.WithTotalCountLogicoutput option (internal/output/options.go): When the response's top-leveltotal_countis0, the field is stripped from the payload viautils.DeleteFields(maxDepth=10) so the caller does not see a misleading zero (typically emitted by the server as a placeholder before the real count is computed). Wired intomeeting search,record search, andrecord list. Non-zero counts are preserved as-is. - New
cmdutil.FormatMeetingCodenormalization helper and companion unit tests (internal/cmdutil/meeting_help.go,internal/cmdutil/meeting_help_test.go): Accepts the four common ways users copy a meeting code —#腾讯会议:295-150-176,295-150-176,295 150 176,295150176— and reduces every form to the bare digit string. Strips the#腾讯会议:/#腾讯会议:prefix (full-width and half-width colon), then removes hyphens, spaces, tabs, and single quotes via a singlestrings.NewReplacer. Applied at every entry point that accepts a meeting code:meeting get,meeting search,record list,record search. Replaces the ad-hoc single-quote stripping added in v1.0.13. - JSON output stops HTML-escaping
</>/&(internal/output/print.go): New internalmarshalNoEscapehelper wrapsjson.EncoderwithSetEscapeHTML(false)and strips the trailing newline thatEncoder.Encodeappends, so the top-levelFormatPrintoutput preserves URLs, query strings, and other verbatim text exactly as the server returned them. Bothjsonandjson-prettyformats use the new helper. - New
output.MetaKeyFilterFieldexported constant (internal/output/options.go): The v1.0.12metaKeyFilterFieldsentinel is now exported so downstream callers (and the SKILL / docs) can reference the reservedfilter_fieldmeta key by name instead of hard-coding the string.
Changed
meeting getandrecord listnow normalize--meeting-codeviacmdutil.FormatMeetingCode(cmd/meeting/get.go,cmd/record/list.go): Previously these commands forwarded the flag verbatim, so#腾讯会议:295-150-176or295 150 176would silently miss on the server. They now share the same normalization pipeline as the newsearchcommands.record listoutput pipeline gainsWithTotalCountLogic(cmd/record/list.go): The option chain becomesWithCompact → WithConvert → WithTotalCountLogic, so a server-reportedtotal_count = 0is stripped from the response, matching the new search commands' behavior.- SKILL bumped to 1.0.14 with a new "list vs. search" decision guide and hardened pagination / contact / write-op rules (
skills/tmeet-skill/SKILL.md,skills/tmeet-skill/references/tmeet-meeting.md,skills/tmeet-skill/references/tmeet-record.md,skills/tmeet-skill/references/tmeet-contact.md,skills/tmeet-skill/references/tmeet-control.md,skills/tmeet-skill/references/tmeet-report.md):- New "查询命令选择准则(list vs search)" section: A decision table mandates that any query carrying a keyword (subject / meeting code / creator / note / …) — even when combined with a time window — must go through
meeting searchorrecord search; pure-time queries stay onlist/list-ended/record list. Explicitly forbids "cramming" keyword conditions intolistcommands and calls out the composite-condition case ("上周和张三的会议" →--query "张三" --start ... --end ...), plus a "when in doubt, ask" rule for ambiguous numeric inputs (meeting code vs. meeting id). - Pagination discipline tightened: Unless the user has explicitly asked for "all / 全部 / 所有 / 完整" results, the model must not silently follow
next_page_tokenacross pages; it must first show the current page's key info together with a "there is more" hint and ask the user before paging further. The deprecated-parameters list is corrected to--page/--pos/--size, and a new callout clarifies thatrecord transcript-get's--pid/--limitare not pagination and are not deprecated. - Contact commands demoted from "member lookup" to "openId resolver":
contact search/contact lookup-by-phone/contact lookup-by-emailare now strictly limited to two downstream actions — meeting invitation (meeting invitees-add/meeting invitees-replace) and calling a non-participant into the meeting (control call). A new "调用前置门禁" self-check requires the model to confirm the current turn contains such a downstream action before invoking any contact command; when the user's request stops at "who is this person / their department / their phone / do they exist", the model must refuse with a fixed rejection template rather than call the contact API. The "back-fill invitee names viacontact search" affordance from v1.0.8 has been removed.tmeet-contact.mdgains the front-matter block and rejection template;tmeet-meeting.md's invitee-mutation reply template no longer permits falling back tocontact searchfor name resolution. meeting invitees-addadded to the confirm-before-execute table andmeeting update's reference doc now carries an explicit "写操作,执行前请确认用户意图" callout listing "which field is changing from A to B" as part of the required confirmation payload.report participantsreference doc now documents that the endpoint returns all attendees (both still-in-meeting and already-left) and thatleft_timebeing empty means "still in meeting" vs. non-empty meaning "already left at that time"; a matching "returned more people than expected" entry is added to the troubleshooting table.tmeet-control.mdno longer opens with the generic "look up members viacontact search" line; the guidance is scoped down and moved inline to thecontrol callsection only.
- New "查询命令选择准则(list vs search)" section: A decision table mandates that any query carrying a keyword (subject / meeting code / creator / note / …) — even when combined with a time window — must go through
Removed
- v1.0.13's ad-hoc single-quote sanitization in the meeting-code path is gone — its behavior is now subsumed by the more general
cmdutil.FormatMeetingCodehelper, which also handles the#腾讯会议:prefix, hyphens, spaces, and tabs.
[v1.0.13] - 2026-07-22
Added
- New
internal/crashpackage: CLI panic capture and server-side reporting (internal/crash/recover.go,internal/crash/reporter.go): Introduces a global panic safety net for the CLI process.crash.RecoverAndReport(ctx, tmeet, r)— Handles a value already recovered by the caller's deferred closure. Captures the panic value together withruntime.Version()/runtime.GOOS/runtime.GOARCHanddebug.Stack(), head-truncates the resulting stack tomaxCrashStackLen = 8192bytes to avoid oversized request bodies, forwards it toReport, prints a single-lineError: tmeet crashed: <value>to stderr, and returns(PanicExitCode, true). The function intentionally does not re-panic and must not callos.Exititself so that sibling defers (most importantlylog.Close()) still run during the normal function unwind.recover()is required to be called directly inside the caller's deferred closure — moving it intoRecoverAndReportwould makerecover()returnniland let the panic keep propagating.crash.Report(ctx, tmeet, errorCode, crashStack)— Best-effort POST to/v1/cli/crashvia the existing REST proxy (so it inherits auth, retry, and theTmeet-Cli-Ver/Tmeet-Tracecommon headers;cli_versionandcli_cmdare therefore deliberately omitted from the body). Wrapped in an innerdefer recover()for defence in depth, guarded againstniltmeet/tmeet.UserConfig, and bounded by an independent 1-secondreportTimeoutso a slow network never delays CLI exit. On failure the error is silently downgraded to alog.Warnfline.crash.PanicExitCode = 2— Dedicated exit code for panic-terminated runs, deliberately distinct from the1used for regular business failures so operators can tell crashes apart from expected errors by exit code alone.
- New
ClientCodePanic = 9000client error code (internal/exception/client_code.go): Sits above the existing2xxx/3xxxclient-error ranges and is used as theerror_codefield of the/v1/cli/crashpayload.
Changed
cmd.Execute()wired into the crash safety net and switched to a named return value (cmd/root.go):Execute()now returns(exitCode int)as a named return so the deferred panic handler can override the exit code without breaking the normalreturn 0/return 1flow.log.Init/defer log.Close()is registered before the crash-recoverydefer, relying on LIFO defer ordering so thatRecoverAndReport(which itself logs via the file logger and the REST proxy) finishes beforelog.Close()runs — writing to an already-closed logger would panic and be silently swallowed by the reporter's innerrecover(). The top-leveldeferclosure ownsrecover()and passes its result intocrash.RecoverAndReport; when the return value indicates a crash,exitCodeis set tocrash.PanicExitCode. All existing subcommand error paths continue to return1vialog.Errorf+return 1.crash.Reportbody simplified (internal/crash/reporter.go):operator_idis read directly fromtmeet.UserConfig.OpenIdwith a fixedoperator_id_type = 2(openId), removing an earlier intermediate config-lookup helper. Whentmeet.UserConfigisnil(i.e. the user has never logged in), the report is skipped instead of sending an empty operator, since the server cannot attribute an anonymous crash to a specific installation anyway.- SKILL bumped to 1.0.9 and
auth loginguidance rewritten (skills/tmeet-skill/SKILL.md,skills/tmeet-skill/references/tmeet-auth.md): The v1.0.12 note already flippedauth loginfrom background-only to foreground-only; this release finishes that migration in the reference doc: the2>&1 &background examples are replaced with plain foreground invocations, and a new bullet clarifies thatauth loginprints the authorization URL and then blocks for up to 300 s waiting for the browser callback, so backgrounding it detaches the process from the controlling terminal and prevents credentials from being persisted. Two additional agent-facing rules were added: (1) on Hermes without a default browser, the agent must not invoketmeet auth loginitself and must instead instruct the user to run it manually in a terminal; (2) on firstauth login(or whenever the model changes), the agent must write the current agent identifier (Cursor / Claude Desktop / Cline / CodeBuddy / …) and model name (Claude 3.5 Sonnet / GPT-4o / DeepSeek / …) intoTMEET_AGENT/TMEET_MODELwithout asking the user.
[v1.0.12] - 2026-07-20
Added
- Server-driven response field filtering for
report participants(cmd/report/participants.go,internal/output/options.go): The downstream API may now return a top-levelfilter_fieldstring in the response body — a comma-separated list of field names or dot paths (e.g.participants.email,participants.phone) that the CLI must strip before exposing the data to the caller. The behavior is entirely driven by the server and is not surfaced as a CLI flag; thefilter_fieldmeta field itself is always removed so it never leaks out. This lets the backend tighten per-endpoint field visibility without a CLI release. - New
output.WithMetaFieldFilter(metaKey)output option and exportedoutput.MetaKeyFilterFieldconstant (internal/output/options.go): Generic "strip a reserved meta key from the response" helper, with a documented special case: whenmetaKey == "filter_field", the value of that meta key is additionally parsed as a comma-separated field list and each entry is stripped from the response viautils.DeleteFieldswithmaxDepth=10. Wired intoreport participantsand layered beforeWithConvert/WithCompactin the option chain, so field renaming and compact trimming operate on the already-filtered payload. - New
internal/cmdutil/apicmdctxsub-package (internal/cmdutil/apicmdctx/apicmdctx.go): Extracts the "resolved ApiCmd on context" primitive out ofinternal/proxy/rest-proxyinto a dependency-free sub-package. Exposesapicmdctx.Inject(ctx, apiCmdName)/apicmdctx.Get(ctx)with an unexported key type. Bothinternal/cmdutil/middlewareandinternal/proxy/rest-proxynow depend on this package instead of the middleware depending on the proxy, breaking the previous import cycle risk.
Changed
WithApiCmdmiddleware and REST proxy switched toapicmdctx(internal/cmdutil/middleware/api_cmd.go,internal/proxy/rest-proxy/proxy.go,cmd/tshoot/log.go):middleware.WithApiCmdnow writes the resolved ApiCmd viaapicmdctx.Inject; the REST proxy reads it viaapicmdctx.Getwhen composing theTmeet-Cli-Nameheader;tshoot log's upload flow also injectsApiCmdTshootLogUploadviaapicmdctx.Injectinstead of the oldrestProxy.InjectApiCmdContext. The v1.0.11restProxy.InjectApiCmdContext/restProxy.GetApiCmdFromContexthelpers have been removed from the proxy package.report participantsoutput option ordering hardened (cmd/report/participants.go): ReorderedFormatPrintoptions toWithMetaFieldFilter → WithConvert → WithCompact, so server-side field stripping happens first, then enum/base64 conversion runs on the trimmed payload, then--compactwhitelist filtering is applied last. This gives a well-defined single "filter → transform → project" pipeline instead of the ad-hoc pre-FormatPrintJSON manipulation that lived inparticipants.gobefore.math/randupgraded tomath/rand/v2(internal/core/thttp/authenticator.go,internal/proxy/rest-proxy/proxy.go): The auth nonce generator and the REST proxy's random component now usemath/rand/v2's auto-seeded top-levelrand.IntN, removing the manualrand.New(rand.NewSource(time.Now().UnixNano()))boilerplate. Behavior is unchanged; the code paths are simpler and per-goroutine safe.WithFilterFieldsoutput option removed (internal/output/options.go): The v1.0.10output.WithFilterFields([]string)variant — which required the caller to hard-code the list of fields to strip — has been deleted; its use case is now covered by the more flexible, server-drivenWithMetaFieldFilter. No caller in this repository relied on the old option.- SKILL bumped to 1.0.8 (
skills/tmeet-skill/SKILL.md): Theauth loginguidance has been rewritten. The previous instruction "must run in the background with2>&1 &" was wrong — background execution detaches the process from the controlling terminal and causes the OAuth callback to fail to persist credentials. The new guidance clarifies thatauth loginprints the authorization URL and then blocks for up to 300 seconds waiting for the user to complete the browser authorization, and that it must be run in the foreground. Related wording tweaks were also applied to the top-level description.
[v1.0.11] - 2026-07-08
Added
- AI agent identity tracking via
AgentConfig(internal/config/agent.go,internal/config/agent_test.go): New module that records the calling AI agent name and LLM model in plaintextagent.json, separate from user credentials. Provides atomic write (tmp + rename), idempotent clear, andGetAgentConfig/SaveAgentConfig/ClearAgentConfigAPIs. Companion unit tests inagent_test.go(9 test cases: missing file, round-trip, nil save, overwrite, clear, idempotent clear, corrupted JSON, path resolution, atomic write content). auth loginnow persists agent identity (cmd/auth/login.go): After successful login, readsTMEET_AGENT/TMEET_MODELenvironment variables and saves them toagent.json. Write failure is logged but non-fatal — login succeeds regardless.auth statusnow displaysUserName(cmd/auth/status.go): When the refresh token is still valid, calls/v1/cli/get-user-infoto fetch and display the user's name alongsideOpenId. Re-reads user config after the API call to reflect any token refresh that occurred in-flight.meeting updatesupports--sub-meeting-idfor single sub-meeting modification (cmd/meeting/update.go): New--sub-meeting-idflag (recurring meetings only) modifies a single sub-meeting's start/end time without altering the recurring rule. Mutually exclusive with--recurring-type/--until-type/--until-count/--until-date— validated at runtime with a clear error message. ExtractedbuildRecurringRule()helper for cleaner payload assembly.Tmeet-Cli-Namerequest header and ApiCmd context passthrough (internal/proxy/rest-proxy/proxy.go,internal/cmdutil/middleware/api_cmd.go): The REST proxy now attaches aTmeet-Cli-Nameheader carrying the resolved ApiCmd identifier. NewInjectApiCmdContext/GetApiCmdFromContextutilities use an unexported key type. TheWithApiCmdmiddleware writes the resolved ApiCmd to both the cobra annotation andcmd.Context(), decoupling the proxy from annotation reads.- New ApiCmd constants for tshoot commands (
internal/cmdutil/api_schema.go):ApiCmdTshootLogUpload(covers the two-step log upload flow: fetch upload token + notify upload complete) andApiCmdTshootFeedback.tshoot feedbackis now wrapped withWithApiCmdmiddleware (cmd/tshoot/feedback.go);tshoot loginjectsApiCmdTshootLogUploadinto context before the upload flow (cmd/tshoot/log.go).
Changed
AccessTokenproactive refresh with 60-second leeway (internal/auth/auth.go): IntroducedaccessTokenRefreshLeeway = 60s.RefreshTokennow triggers 60s before the declared expiry to absorb clock skew and in-flight network latency, preventing server-sideTokenExpiredrejection that the rest-proxy treats as fatal (clears local credentials).GetSystemInfoaccepts*config.AgentConfig(internal/common/system.go,internal/common/system_test.go): Agent and Model are now resolved via env var (TMEET_AGENT/TMEET_MODEL) →agent.jsonfallback → empty string. All test call sites updated to passnil.control kick --allow-rejoindefault flipped totrue(cmd/control/kick.go): The flag now defaults totrue(allow rejoin). Users must explicitly pass--allow-rejoin=falseto disallow rejoin. Help text updated accordingly.internal/tmeet.goloadsAgentConfigat startup (internal/tmeet.go): Missingagent.jsonis treated as "not set" and does not block CLI startup.GetSystemInfois called with the loaded config so agent/model metadata is available for all subsequent REST requests.- README and SKILL references synced (
README.md/README_EN.md/skills/tmeet-skill/SKILL.md/skills/tmeet-skill/references/tmeet-auth.md/skills/tmeet-skill/references/tmeet-control.md/skills/tmeet-skill/references/tmeet-meeting.md): Added--sub-meeting-idparameter tables and usage examples tomeeting updatesections. Updated--allow-rejoindefault value across all documentation surfaces.auth statusoutput example now includesUserName.control kickdescription simplified inSKILL.md. SKILL bumped to 1.0.7.
[v1.0.10] - 2026-06-19
Added
meeting create/meeting updatenow expose four advanced meeting-settings flags (cmd/meeting/create.go,cmd/meeting/update.go):--water-mark-type(text watermark,0-single row /1-double row /2-off, default2; when not2,allow_screen_shared_watermarkis set in tandem),--audio-watermark,--auto-record-type(none/local/cloud), and--auto-asr. These fields are only written to the requestsettingswhencmd.Flags().Changed(...)reports they were explicitly provided, so unspecified flags will not silently override the enterprise defaults; explicitly turning a bool flag off requires the=form, e.g.--audio-watermark=false.- Hints output channel for enterprise-locked settings (
internal/cmdutil/hints/,internal/cmdutil/meeting.go,internal/output/): Introduced theHintProviderinterface andoutput.WithHints(fn)option, and added a top-levelhintsfield toformatOutput.meeting create/meeting updateplug into this channel: when the responsesettings.corp_lock_maskis non-zero, the output emits messages likeEnterprise has locked the [Text Watermark] setting, covering text watermark / audio watermark / auto recording / auto speech recognition. Both top-levelsettingsandmeeting_info_list[].settingsresponse shapes are supported, with duplicate entries deduplicated. corp_lock_maskbitmask enum (internal/utils/enumerate/corp_lock_mask.go): Defines four constants (bit0~bit3) withCorpLockMaskName(single bit → label) andCorpLockMaskNames(combined mask → labels in canonical bit order). Companion unit tests incorp_lock_mask_test.go.meeting_join_type/show_all_sub_meetingsenum mappings (internal/utils/enumerate/):1=all/2=invited/3=internaland0=no/1=yes. The newMeetingJoinTypeConverter/ShowAllSubMeetingsConverterare wired intomeeting list(foronly_user_join_type,is_show_all_sub_meetings) and intomeeting create/meeting updateoutputs (foronly_user_join_type).- New
utils.DeleteFieldsJSON blacklist utility andoutput.WithFilterFieldsoutput option (internal/utils/filter.go,internal/output/options.go): The dual of the v1.0.5KeepFieldswhitelist — supports both field-name mode and dot-path mode (arrays auto-expand) and usesmaxDepthto bound recursion;WithFilterFieldsexposes it to the output layer withmaxDepth=10.
Changed
meeting updatehelp-text fix (cmd/meeting/update.go): Corrected the--recurring-typehelp text from1-weekdayto1-weekdays, aligning it withmeeting createand the actual semantics.- README and SKILL references synced for the new advanced meeting-settings flags (
README.md/README_EN.md/skills/tmeet-skill/references/tmeet-meeting.md): Parameter tables and examples for--water-mark-type/--audio-watermark/--auto-record-type/--auto-asradded to themeeting create/meeting updatesections, with two notes called out: (1) personal vs. enterprise (organization) accounts behave differently depending on whether the enterprise has set the option to "forced"; (2) explicitly turning a bool flag off requires the=falseform. - SKILL bumped to 1.0.6 (
skills/tmeet-skill/SKILL.md).
[v1.0.9] - 2026-06-12
Added
meeting createnow supports inviting participants at creation time (cmd/meeting/create.go): A new--inviteesflag accepts anopen_idlist (comma-separated or repeated, max 100), reusing v1.0.8'scmdutil.PackageApiInviteesUsersfor dedup and upper-bound checks.meeting updatenow supports mutating the invitee list as part of the update call (cmd/meeting/update.go): Added the--invitees+--invitees-typeflag pair, supportingadd/remove/replacestrategies; both flags must be set together. Complements the v1.0.8meeting invitees-add/remove/replacesubcommands by avoiding a second round-trip when the meeting body and the invitee list change in the same operation.
Changed
- REST proxy error handling and retry policy adjusted (
internal/proxy/rest-proxy/proxy.go): HTTP408/504gateway timeouts are now classified asNetworkErrorto trigger retries; all other non-200 responses (except token expiration) are uniformly classified asNotRetryRequestErrorand no longer retried. The "should-retry" decision shifts from a server-business-code allowlist to the more conservative "anything other than a network error is not retried", reducing the risk of write operations (calls, kicks, invitee mutations) being silently replayed. - README and SKILL references synced for the new invitation flags (
README.md/README_EN.md/skills/tmeet-skill/references/tmeet-meeting.md): Parameter tables and examples for--invitees/--invitees-typeadded to themeeting create/meeting updatesections. - SKILL bumped to 1.0.5 (
skills/tmeet-skill/SKILL.md).
Removed
- Removed the "specific business-code non-retry" allowlist mechanism introduced in v1.0.7 (
internal/exception/server_code.go): Deleted theServerCodeRecordNotExist (500277)constant, thenotRetryCodeMap, and theIsNotRetryCodefunction; their semantics are now subsumed by the broader "non-200 is never retried" policy above.
[v1.0.8] - 2026-06-11
Added
- New
contactsubcommand group (cmd/contact/): Address-book lookup capabilities, primarily intended to resolve names ↔open_idfor the "meeting invitation" and "in-meeting call" scenarios.contact search(cmd/contact/search.go) — Search enterprise address-book members by username, with optional job-title / department filtering when the username matches too many people; when only a single member is matched, the response is trimmed to keepopen_idonly (via the newoutput.WithContactSearchLogichook).--username(required) — Username to search--job-title(optional) — Job title filter when the username yields too many matches--department-name(optional) — Department filter when the username yields too many matches
contact lookup-by-phone(cmd/contact/lookup_by_phone.go) — Batch-look up users by phone number; up to 50 numbers per call; each number is pre-validated byutils.ValidatePhonebefore the request is sent.--phones(required) — Phone-number list, comma-separated or repeat the flag, max 50
contact lookup-by-email(cmd/contact/lookup_by_email.go) — Batch-look up users by email address; up to 50 emails per call; each email is pre-validated byutils.ValidateEmailbefore the request is sent.--emails(required) — Email list, comma-separated or repeat the flag, max 50
- New
controlsubcommand group (cmd/control/): In-meeting real-time control capabilities.control call(cmd/control/call.go) — In-meeting batch call to bring members into the meeting, up to 20 members per call. Classified as a write operation and listed in the SKILL dangerous-operations table.--meeting-id(required) — Meeting ID--users(required) — Memberopen_idlist to call
control kick(cmd/control/kick.go) — Kick members out of an ongoing meeting, supporting three identity types (regular member / SIP / PSTN), capped at 20 in total per call. Classified as a write operation, listed in the SKILL dangerous-operations table, and SKILL further enforces a hard rule that the kick targets must come fromreport participantsand must not come from anycontactlookup result.--meeting-id(required) — Meeting ID--allow-rejoin(optional) — Allow kicked members to re-join--users/--sip-users/--pstn-users(at least one required) — Map respectively to regular members'open_id, SIP devices'ms_open_id(instanceid=9), and PSTN devices'ms_open_id(instanceid=0)
- New invitee-management subcommands under
meeting(cmd/meeting/): Building on the existinginvitees-listfrom v1.0.7, this release adds incremental add / remove / full-replace capabilities.meeting invitees-add(cmd/meeting/invitees_add.go) — Append invitees to an existing meeting.meeting invitees-remove(cmd/meeting/invitess_remove.go) — Remove specified invitees from an existing meeting. Classified as a write operation and listed in the SKILL dangerous-operations table.meeting invitees-replace(cmd/meeting/invitees_replace.go) — Replace the meeting's invitee list with a new one; an empty list clears all invitees. Classified as a write operation and listed in the SKILL dangerous-operations table.- All three share
--meeting-id(required) and--invitees(theopen_idlist to apply, comma-separated or repeat the flag, max 100);--inviteesis required forinvitees-add/invitees-removeand optional (empty meaning "clear all") forinvitees-replace.
- New shared user-list packagers for invitee / in-meeting-control commands (
internal/cmdutil/users_help.go): Extracted three reusable helpers —PackageApiInviteesUsers(invitees, capped byInviteesListMax = 100),PackageMeetingControlUsers(regular members,open_id,to_operator_id_type=2), andPackageMeetingControlSpecialUsers(SIP / PSTN,ms_open_id,to_operator_id_type=4plusinstanceid) — that uniformly handle dedup, empty-value validation, and upper-bound checks. The constantMeetingControlUsersListMax = 20is also defined here. - New phone / email format validation utilities (
internal/utils/validate.go):ValidatePhone— 11-digit phone-number regex check (^1[3-9]\d{9}$).ValidateEmail— Total length ≤ 100; must contain@with non-empty local / domain parts; rejects quoted local part, consecutive dots, and IP-address domains; finalizes with a regex format check.SplitAndTrim— Generic "split-by-comma + trim + drop empty" helper.- These helpers are wired in as pre-call validators for
cmd/contact/lookup_by_phone.go/cmd/contact/lookup_by_email.go. - Companion unit tests in
internal/utils/validate_test.gocover valid, invalid, and boundary cases for both formats.
- New
output.WithContactSearchLogicoutput option (internal/output/options.go): Applied tocontact searchresponses; when theusersarray contains exactly one entry, only theopen_idfield is preserved, avoiding leakage of unnecessary personal information to the agent (mirroring the SKILL constraint that the address book is not a general-purpose people-info lookup). - New API Schema identifiers (
internal/cmdutil/api_schema.go): Added the ApiCmd constantsmeeting_invite_add/meeting_invite_remove/meeting_invite_replace/contact_search/contact_lookup_by_phone/contact_lookup_by_email/control_call/control_kick, so the new commands can plug into the v1.0.5--compactand middleware pipeline.
Changed
- Root command registers the
contactandcontrolsubcommand groups (cmd/root.go):contact.NewBaseCmdandcontrol.NewBaseCmdare added between the existingauth/meeting/report/record/tshootregistrations. - SKILL bumped to 1.0.4 (
skills/tmeet-skill/SKILL.md):- Top-level
descriptionnow mentions the "address book" and "in-meeting control" capabilities. - The command tree is updated with
meeting invitees-add/remove/replace, the newcontactgroup, and the newcontrolgroup. - The dangerous-operations table now includes
meeting invitees-remove,meeting invitees-replace,control call, andcontrol kick, all requiring user confirmation before execution. - Added a "
contact searchis restricted to specific scenarios" rule: it may only be used for meeting invitations, in-meeting calls, and back-filling invitee names — not as a general-purpose people-info lookup. - Added a "hard rule on kick-target source": the
open_id/ms_open_idpassed tocontrol kickmust come fromreport participants, and must not come from anycontactlookup result. - Added a "multiple results require user confirmation" rule: when commands such as
contact searchreturn more than one candidate, the model must list them and let the user choose, rather than picking one on its own. - Added a "reply template for meeting-invitee mutations": after
meeting invitees-add/remove/replacesucceeds, the reply must follow a strict "topic / time / meeting code / join URL / current invitees" template, and the invitee list must show names rather thanopen_ids, looking them up viameeting invitees-list+contact searchwhen needed.
- Top-level
- SKILL reference docs added / synced (
skills/tmeet-skill/references/): Addedtmeet-contact.mdandtmeet-control.md;tmeet-meeting.mdis updated with parameter tables and usage notes forinvitees-add/invitees-remove/invitees-replace. - README sync (
README.md/README_EN.md): Command tree and command reference updated withmeeting invitees-add/remove/replace,contact search/lookup-by-phone/lookup-by-email, andcontrol call/kick, including the corresponding parameter descriptions and examples.
[v1.0.7] - 2026-06-10
Added
- New
record permission-apply-preparecommand (cmd/record/permission_apply_prepare.go): Whenrecord address/record smart-minutes/record transcript-*fail due to missing permission, this command fetches the approval preview (approval text, meeting topic, recording owner, request notes, etc.) so the agent can show it to the user for confirmation before any write call.--meeting-record-id(required) — Meeting recording ID--meeting-id(optional) — Meeting ID
- New
record permission-apply-commitcommand (cmd/record/permission_apply_commit.go): Submits the recording-permission approval after user confirmation; returnsunique_id/status/approval_url/share_text. Classified as a write operation and listed in the SKILL dangerous-operations table.--meeting-record-id(required) — Must match the value used inprepare--meeting-id(optional) — Meeting ID
- Non-retryable server error mechanism (
internal/exception/): IntroducedServerCodeRecordNotExist (500277)together withnotRetryCodeMap/IsNotRetryCode(code); added a new client-side errorNotRetryRequestErrorand error codeClientNotRetryRequest (2008).
Changed
- REST proxy retry policy (
internal/proxy/rest-proxy/proxy.go):RetryIfnow also excludesNotRetryRequestErrorin addition toTokenExpiredError; server-side business errors that hitIsNotRetryCodeare surfaced asNotRetryRequestErrorimmediately, avoiding pointless retries on cases such as "recording does not exist". - Documentation sync:
README.md/README_EN.mdcommand tree and command reference updated with the two new permission-apply commands;skills/tmeet-skill/SKILL.mdbumped to1.0.3with a "recording permission apply" capability note andrecord permission-apply-commitadded to the dangerous-operations list requiring user confirmation;skills/tmeet-skill/references/tmeet-record.mdadds the "permission apply workflow" (prepare → user confirm → commit → showapproval_url) and error-handling guidance.
[v1.0.6] - 2026-06-03
Added
- New
tshoot feedbackcommand (cmd/tshoot/feedback.go): Lets the agent (or end user) report troubleshooting feedback to the server, closing the loop between CLI usage and product iteration.--category(required) — Feedback category, one of:tool_not_found/tool_error/tool_inadequate/unexpected_result/suggestion--intent(required) — Original intent of the agent (max 200 characters)--actions-tried— Actions the agent has already tried (max 500 characters)--result— Result or blocker of the tried actions (max 500 characters)--tool-name— Tool/command name involved in the feedback--error-code— Error code returned by the tool, if any
- New
CharacterLimitstring validation utility (internal/utils/string.go): UTF-8 rune-count based length check; returnsInvalidArgsErrorwith the offending flag name, the configured limit, and the actual length when exceeded. Used bytshoot feedbackto enforce the--intent/--actions-tried/--resultlength caps. - Unit tests for
CharacterLimitcovering ASCII, multi-byte (CJK), boundary, and over-limit cases (internal/utils/string_test.go).
Changed
- Error logging on failure paths:
cmd/root.gonow logsexecute failed: %vvialog.ErrorfwheneverrootCmd.Execute()returns a non-nil error, andinternal/auth/auth.gologsrefresh token failed: %vbefore clearing the local config inTmeetAuth.RefreshToken. Combined with the file-based logging introduced in v1.0.4, these traces are now recoverable throughtmeet tshoot log. cmd/tshoot/base.goregisters the newfeedbacksubcommand alongsidelogunder thetshootgroup.- Updated
README.md/README_EN.md,skills/tmeet-skill/SKILL.md, andskills/tmeet-skill/references/tmeet-tshoot.mdwithtshoot feedbackusage, parameter tables, and category guidance.
[v1.0.5] - 2026-05-16
Added
- New global
--compactoutput mode: Added a persistent--compactflag on the root command. When enabled, responses keep only the key fields advertised by the server and strip redundant content. Wired intomeeting list/meeting list-ended/meeting invitees-list/record list/report participants/report waiting-room-log. - New Cobra command middleware framework (
internal/cmdutil/middleware/): ProvidesChain(onion-model composition),WithApiCmd(resolves the ApiCmd and writes it to the command annotation), andWithCompact(fetches the schema for the resolved ApiCmd and propagates the compact-field whitelist via context). All API-backed subcommands'RunEare now built on this chain. - New API Schema module (
internal/cmdutil/): Pulls per-ApiCmd compact fields, field metadata, and cache policy from/v1/api/compact-schema. Ships withApiCmdResolver(StaticApiCmd/FlagSwitch/ResolverFunc), command annotation helpers (annotations.go), and pagination helpers (pagination.go:ClampingPageSize/ChoosePageOrToken/ChoosePosOrToken). - New local file cache primitive (
internal/core/filecache/): TTL-based file KV cache; cross-process safety viacore/filelockwrite locks, in-process safety via per-key mutex; writes use temp-file + rename for atomic replacement; corrupted files transparently degrade to a miss. Used as the on-disk store for API Schema responses. - New
KeepFieldsJSON whitelist utility (internal/utils/filter.go): Recursively keeps only the specified fields, supporting both field-name mode and dot-path mode (e.g.meeting.recurring_rule.recurring_type); the underlying primitive that powers--compactfield trimming. - New
MeetingUserJoinRoleConverterenum mapping: Convertscreator/hoster/inviteeinto "创建者 / 主持人 / 被邀请者" Chinese display names; applied inmeeting list. - Options-style output layer:
output.FormatPrintnow accepts variadic...Option, with two built-ins —WithCompactandWithConvert— moving compact trimming and field conversion out of individual commands and into the output layer. - Unit tests covering the middleware, file cache (concurrent writes, expiration, corruption fallback, cross-process locking),
KeepFields, and the new enum.
Changed
- Unified pagination flags to
--page-token+--page-size: All paginated commands now use the new pagination protocol (page_type=1); the legacy--page/--posflags are marked deprecated (still honored but with lower priority than--page-token).--page-sizedefaults / caps are aligned accordingly:meeting list/meeting list-ended/meeting invitees-list/record list/record address: default raised to30, max unified to30;report participants/report waiting-room-log: default raised to100, max100.
- All API subcommands now flow through the middleware chain:
RunEis built asmiddleWare.Chain(opts.Run, WithApiCmd(...), WithCompact(...)), and the per-commandutils.ConvertFields(...)calls have been pushed down intooutput.FormatPrint(..., WithConvert(...)). - Field mapping tweaks for
meeting list: Removed the base64 decoding oftime_zone(the server now returns plaintext); added thejoin_meeting_roleChinese mapping; dropped the no-longer-requiredcursoryquery parameter. - Annotation migration for
auth login/auth status: The hand-writtencmd.Annotations = map[string]string{"skipPreCheck": "true"}has been replaced withcmdutil.InjectSkipPreCheckAnnotation(cmd)so that annotation keys are managed in one place. - Updated
README.md/README_EN.mdwith a new "Pagination" section, the--compactdescription, and a per-command--page-sizedefault/max quick-reference table;skills/tmeet-skill/reference docs synced to the new pagination flags and defaults.
[v1.0.4] - 2026-04-24
Added
- New
tshoot logcommand (cmd/tshoot/): Autonomous log-based troubleshooting supporttshoot log— Query and package local log files for self-service troubleshooting--start/--end— Filter logs by ISO 8601 time range--upload— Package and upload logs to the server (login required); returns alog_id- Without
--upload, logs are packaged into a zip file and saved to the user's home directory - Supports pre-filtering by date in file names + precise filtering by in-line timestamps
- Upload flow: fetch upload token → PUT file to COS → notify server of upload completion
- New file-based logging module (
internal/log/):logging.go— Structured file logging with four levels: DEBUG / INFO / WARN / ERROR- Daily log rotation with 10 MB per-file size limit and auto-incrementing sequence numbers
- Automatic cleanup of log files older than 7 days
- Asynchronous writes via background goroutine; callers are never blocked
- Multi-process safety: atomic writes with
O_APPEND+ cross-process file lock for rotation trace_id.go— TraceID generator (8-char timestamp + 4-char PID + 20-char random hex), propagated via context
- New
CalcFileInfoutility (internal/utils/file.go): Compute file size, SHA256, and MD5 in a single pass - New error code
ClientUploadToCos (2007)andUploadToCosErrorerror definition (internal/exception/) - Unit tests for logging module, TraceID generator, and file utility functions
Changed
- Refactored
logpackage →outputpackage: Renamedinternal/log/log.gotointernal/output/print.go; package name changed fromlogtooutput. All 16 call sites acrosscmd/auth/,cmd/meeting/,cmd/record/,cmd/report/updated with the new import path - Enhanced
cmd/root.go: Initialize file logging system (log.Init) withdefer log.Close(); registertshootsubcommand group; addcommandOperationLog()to record operation logs after command execution; inject TraceID into command context viarootCmd.SetContext() - Enhanced
internal/tmeet.go: Inject TraceID viacontext.WithValueinNewTmeet(); addTCtxfield - Enhanced REST proxy (
internal/proxy/rest-proxy/proxy.go): Add file logging for HTTP requests and responses - Updated
README.md/README_EN.mdwithtshoot logcommand usage instructions - Updated
skills/tmeet-skill/with newtmeet-tshoot.mdreference documentation
[v1.0.3] - 2026-04-16
Added
- Auto-open browser on
auth login: After obtaining the OAuth2 authorization URL,tmeet auth loginnow automatically attempts to open the system default browser to the authorization page, eliminating the need for users to manually copy and paste the URL - New
--no-browserflag forauth login: Added--no-browseroption to disable auto-opening the browser; when set, only the authorization URL is printed (preserving the original behavior) - Cross-platform browser opening (
internal/core/browser): New platform-specific implementations:- macOS (
browser_darwin.go): Usesopencommand - Windows (
browser_windows.go): Usesrundll32 url.dll,FileProtocolHandler - Linux (
browser_other.go): Usesxdg-openwith graphical environment detection (DISPLAY/WAYLAND_DISPLAY)
- macOS (
- Unit tests for browser package using
fakeExecCommandpattern to mock system commands
Changed
- Updated
README.md/README_EN.mdauth login documentation to describe auto-open browser behavior and the new--no-browserflag - Updated
SKILL.mdwith--no-browserusage example and Hermes agent-specific note for environments without a default browser - Updated
tmeet-auth.mdparameter table to document the--no-browseroption
[v1.0.2] - 2026-04-15
Added
- Enum mapping for API responses: New
internal/utils/enumeratepackage with human-readable name mappings for API enum fields, includingmeeting_type,meeting_status,meeting_recurring_type,meeting_recurring_until_type,meeting_user_role,record_state,record_type,record_audio_detect, etc. - Time-range filtering: New
FilterMeetingsByTimeRangeutility (internal/utils/filter.go) to filter meeting lists by start/end time boundaries - Path-mode field converter: Enhanced
ConvertFields(internal/utils/converter.go) to support dot-separated path mode (e.g.,meeting.recurring_rule.recurring_type) for precise field matching - Comprehensive unit tests for all new enum mappings, converter, and filter utilities
Changed
- Console output optimization: Replaced
cmd.Printlnwithfmt.Fprintln(cmd.OutOrStdout())/fmt.Fprintln(cmd.OutOrStderr())for proper stdout/stderr separation; added structuredFormatPrintfunction withformatOutputstruct supportingtrace_id,message, anddatafields - Refactored all command handlers (
cmd/meeting/*,cmd/record/*,cmd/report/*) to use the new output methods - Renamed
internal/utils/json.go→internal/utils/converter.gowith enhanced path-mode support - Removed unused
FormatPrint/FormatPrintPrettyfrominternal/proxy/rest-proxy/proxy.goand cleaned up unused imports - Moved existing
instanceid.gointo the newenumeratesub-package - Revised descriptions in
README.md,README_EN.md, andskills/tmeet-skill/references; added security risk warnings
Fixed
- Removed unused
installphony target fromMakefile(Fixes #1) - Fixed typos across the codebase
[v1.0.1] - 2026-04-07
Added
Initial release of the tmeet CLI tool for Tencent Meeting (WeMeet).
Authentication (tmeet auth)
auth login— Log in to tmeet via OAuth2 device-code flowauth logout— Log out from tmeetauth status— Show current authentication status
Meeting Management (tmeet meeting)
meeting create— Create a new meeting (supports recurring meetings)meeting get— Get meeting details by meeting ID or meeting codemeeting list— List upcoming meetingsmeeting list-ended— List ended meetingsmeeting update— Update an existing meetingmeeting cancel— Cancel a meetingmeeting invitees-list— Get the invitees list of a meeting
Recording Management (tmeet record)
record list— List meeting recordingsrecord address— Get recording download addressesrecord smart-minutes— Get AI-generated smart minutes from a recordingrecord transcript-get— Get transcript detailsrecord transcript-paragraphs— Get transcript paragraphsrecord transcript-search— Search transcript content by keyword
Meeting Reports (tmeet report)
report participants— Get the participants list of a meetingreport waiting-room-log— Get the waiting room members log
Other Features
- AES-256-GCM encrypted credential storage (no plaintext on disk)
- Cross-platform support (macOS / Linux / Windows)
- File-lock based concurrent access protection
- Symlink attack prevention
- Output format support:
json(default) andjson-pretty