@larktask/aamp-feishu-task-agent 0.1.0-dev.173 → 0.1.1-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,9 @@ APP_ID=""
7
7
  APP_SECRET=""
8
8
  BOT_NAME=""
9
9
  LARK_CLI_PROFILE=""
10
+ AGENT_EXECUTION_LOCATION=""
11
+ AGENT_ATTACHMENT_POLICY=""
12
+ AGENT_TASK_DISPATCH_CONCURRENCY=""
10
13
  AAMP_HOST="https://meshmail.ai"
11
14
  DEBUG_MODE="false"
12
15
  AAMP_TASK_START_MODE="install"
@@ -15,6 +18,7 @@ AAMP_TASK_ENTRY="${AAMP_TASK_ENTRY:-}"
15
18
  AAMP_TASK_INTERNAL="${AAMP_TASK_INTERNAL:-false}"
16
19
  AAMP_TASK_INTERNAL_RESULT_FD="${AAMP_TASK_INTERNAL_RESULT_FD:-3}"
17
20
  AAMP_TASK_INTERNAL_INPUT_FD="${AAMP_TASK_INTERNAL_INPUT_FD:-4}"
21
+ AAMP_TASK_INTERNAL_EXECUTION_LOCATION="${AAMP_TASK_INTERNAL_EXECUTION_LOCATION:-}"
18
22
  NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmjs.org/}"
19
23
  NPM_CACHE_DIR="${NPM_CONFIG_CACHE:-${npm_config_cache:-${TMPDIR:-/tmp}/aamp-one-click-npm-cache}}"
20
24
  NPM_GLOBAL_PREFIX="${NPM_GLOBAL_PREFIX:-$HOME/.aamp/npm-global}"
@@ -47,25 +51,67 @@ CODEM_INSTALLER_URL="${CODEM_INSTALLER_URL:-https://sf-unpkg-src.bytedance.net/@
47
51
  CODEM_INSTALLER_CONFIRM="${CODEM_INSTALLER_CONFIRM:-}"
48
52
  CODEM_PROVIDER_PREFLIGHT="${CODEM_PROVIDER_PREFLIGHT:-true}"
49
53
  CODEM_PROVIDER_PREFLIGHT_TIMEOUT_SECONDS="${CODEM_PROVIDER_PREFLIGHT_TIMEOUT_SECONDS:-60}"
54
+ CODEX_CHATGPT_APP_CLI="/Applications/ChatGPT.app/Contents/Resources/codex"
50
55
  CODEX_APP_CLI="/Applications/Codex.app/Contents/Resources/codex"
51
56
  CODEX_AUTO_UPDATE="${CODEX_AUTO_UPDATE:-true}"
52
57
  CODEX_NPM_PACKAGE="${CODEX_NPM_PACKAGE:-@openai/codex}"
58
+ CODEX_UPDATE_CACHE_FILE="${CODEX_UPDATE_CACHE_FILE:-$HOME/.aamp/feishu-task-agent/codex-update-cache.json}"
59
+ CODEX_UPDATE_CACHE_TTL_SECONDS="${CODEX_UPDATE_CACHE_TTL_SECONDS:-86400}"
53
60
  CODEX_ACP_PKG="${CODEX_ACP_PKG:-@agentclientprotocol/codex-acp@1.0.2}"
61
+ AAMP_TRAE_CLI_BIN="${AAMP_TRAE_CLI_BIN:-}"
62
+ AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS="${AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS:-10}"
63
+ AAMP_TRAECODE_CLI_BIN="${AAMP_TRAECODE_CLI_BIN:-}"
64
+ AAMP_TRAECODE_CHECK_TIMEOUT_SECONDS="${AAMP_TRAECODE_CHECK_TIMEOUT_SECONDS:-10}"
65
+ AAMP_TRAECODE_READINESS_HELPER="${AAMP_TRAECODE_READINESS_HELPER:-}"
66
+ AAMP_TRAECODE_UPDATE_TTY="${AAMP_TRAECODE_UPDATE_TTY:-/dev/tty}"
67
+ TRAEX_INSTALLER_URL="${TRAEX_INSTALLER_URL:-https://code.byted.org/api/tos-proxy/download/traex_install.sh}"
68
+ WORKBUDDY_APP_CLI="/Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy"
69
+ WORKBUDDY_AI_APP_CLI="/Applications/WorkBuddy AI.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy"
54
70
  LARK_REGISTER_APP_SDK="${LARK_REGISTER_APP_SDK:-@larksuiteoapi/node-sdk@1.68.0}"
55
71
  LARK_CLI_MIN_VERSION="${LARK_CLI_MIN_VERSION:-1.0.64}"
56
- FEISHU_APP_SCOPES_TENANT="${FEISHU_APP_SCOPES_TENANT:-im:message,im:message:send_as_bot,im:message:readonly,im:resource,cardkit:card:write,task:task,task:comment,task:task:readonly,task:comment:readonly,task:attachment:delete,task:attachment:file:download,task:attachment:read,task:attachment:upload,task:attachment:write,task:comment:delete,task:comment:read,task:comment:write,task:comment:writeonly,task:task:delete,task:task:read,task:task:write,task:task:writeonly,task:tasklist:delete,task:tasklist:read,task:tasklist:write,task:tasklist:writeonly,search:docs:read,base:app:copy,base:app:create,base:app:read,base:app:update,base:block:create,base:block:delete,base:block:read,base:block:update,base:dashboard:create,base:dashboard:delete,base:dashboard:read,base:dashboard:update,base:field:create,base:field:delete,base:field:read,base:field:update,base:form:create,base:form:delete,base:form:read,base:form:update,base:history:read,base:record:create,base:record:delete,base:record:read,base:record:update,base:role:create,base:role:delete,base:role:read,base:role:update,base:table:create,base:table:delete,base:table:read,base:table:update,base:view:read,base:view:write_only,base:workflow:create,base:workflow:read,base:workflow:update,board:whiteboard:node:create,board:whiteboard:node:read,calendar:calendar.event:create,calendar:calendar.event:delete,calendar:calendar.event:read,calendar:calendar.event:reply,calendar:calendar.event:update,calendar:calendar.free_busy:read,calendar:calendar:create,calendar:calendar:delete,calendar:calendar:read,calendar:calendar:update,contact:user.base:readonly,contact:user.basic_profile:readonly,docs:document.media:download,docs:document.media:upload,docs:document:export,docs:document:import,docx:document:create,docx:document:readonly,docx:document:write_only,drive:drive.metadata:readonly,drive:file:download,drive:file:upload,im:chat.managers:write_only,im:chat.members:read,im:chat.members:write_only,im:chat.moderation:read,im:chat:moderation:write_only,im:message.pins:read,im:message.pins:write_only,im:message.reactions:read,im:message.reactions:write_only,im:message:recall,mail:user_mailbox.event.mail_address:read,mail:user_mailbox.mail_contact:read,mail:user_mailbox.message.address:read,mail:user_mailbox.message.body:read,mail:user_mailbox.message.subject:read,mindnote:node:create,mindnote:node:read,minutes:minutes.basic:read,minutes:minutes.media:export,minutes:minutes:readonly,sheets:spreadsheet.meta:read,sheets:spreadsheet.meta:write_only,sheets:spreadsheet:create,sheets:spreadsheet:read,sheets:spreadsheet:write_only,slides:presentation:create,slides:presentation:read,slides:presentation:update,slides:presentation:write_only,task:custom_field:read,task:custom_field:write,task:section:read,task:section:write,vc:meeting.bot.join:write,vc:meeting.meetingevent:read,vc:meeting.message:write,vc:record:readonly,wiki:member:create,wiki:member:retrieve,wiki:member:update,wiki:node:copy,wiki:node:create,wiki:node:move,wiki:node:read,wiki:node:retrieve,wiki:space:read,wiki:space:retrieve,wiki:space:write_only}"
57
- FEISHU_APP_SCOPES_USER="${FEISHU_APP_SCOPES_USER:-im:message,im:message:readonly,im:resource,cardkit:card:write,task:task,task:comment,task:task:readonly,task:comment:readonly,task:attachment:delete,task:attachment:file:download,task:attachment:read,task:attachment:upload,task:attachment:write,task:comment:delete,task:comment:read,task:comment:write,task:comment:writeonly,task:task:delete,task:task:read,task:task:write,task:task:writeonly,task:tasklist:delete,task:tasklist:read,task:tasklist:write,task:tasklist:writeonly,search:docs:read,search:message,base:app:copy,base:app:create,base:app:read,base:app:update,base:block:create,base:block:delete,base:block:read,base:block:update,base:dashboard:create,base:dashboard:delete,base:dashboard:read,base:dashboard:update,base:field:create,base:field:delete,base:field:read,base:field:update,base:form:create,base:form:delete,base:form:read,base:form:update,base:history:read,base:record:create,base:record:delete,base:record:read,base:record:update,base:role:create,base:role:delete,base:role:read,base:role:update,base:table:create,base:table:delete,base:table:read,base:table:update,base:view:read,base:view:write_only,base:workflow:create,base:workflow:read,base:workflow:update,board:whiteboard:node:create,board:whiteboard:node:read,calendar:calendar.event:create,calendar:calendar.event:delete,calendar:calendar.event:read,calendar:calendar.event:reply,calendar:calendar.event:update,calendar:calendar.free_busy:read,calendar:calendar:create,calendar:calendar:delete,calendar:calendar:read,calendar:calendar:update,contact:user.base:readonly,contact:user.basic_profile:readonly,contact:user:search,docs:document.media:download,docs:document.media:upload,docs:document:export,docs:document:import,docx:document:create,docx:document:readonly,docx:document:write_only,drive:drive.metadata:readonly,drive:file:download,drive:file:upload,im:chat.managers:write_only,im:chat.members:read,im:chat.members:write_only,im:chat.moderation:read,im:chat.nickname:read,im:chat.nickname:write,im:chat.user_setting:read,im:chat.user_setting:write,im:chat:read,im:chat:update,im:chat:create_by_user,im:chat:moderation:write_only,im:feed.flag:read,im:feed.flag:write,im:feed.shortcut:read,im:feed.shortcut:write,im:feed_group_v1:read,im:feed_group_v1:write,im:message.group_msg:get_as_user,im:message.p2p_msg:get_as_user,im:message.pins:read,im:message.pins:write_only,im:message.reactions:read,im:message.reactions:write_only,im:message:recall,mail:event,mail:user_mailbox.event.mail_address:read,mail:user_mailbox.mail_contact:read,mail:user_mailbox.message.address:read,mail:user_mailbox.message.body:read,mail:user_mailbox.message.subject:read,mindnote:node:create,mindnote:node:read,minutes:minutes.artifacts:read,minutes:minutes.basic:read,minutes:minutes.media:export,minutes:minutes.search:read,minutes:minutes.upload:write,minutes:minutes:readonly,minutes:minutes:update,profile:user_profile:read,sheets:spreadsheet.meta:read,sheets:spreadsheet.meta:write_only,sheets:spreadsheet:create,sheets:spreadsheet:read,sheets:spreadsheet:write_only,slides:presentation:create,slides:presentation:read,slides:presentation:update,slides:presentation:write_only,task:custom_field:read,task:custom_field:write,task:section:read,task:section:write,vc:meeting.bot.join:write,vc:meeting.meetingevent:read,vc:meeting.message:write,vc:meeting.search:read,vc:note:read,vc:record:readonly,wiki:member:create,wiki:member:retrieve,wiki:member:update,wiki:node:copy,wiki:node:create,wiki:node:move,wiki:node:read,wiki:node:retrieve,wiki:space:read,wiki:space:retrieve,wiki:space:write_only}"
72
+ AAMP_FEISHU_APP_SCOPES_TENANT_OVERRIDE="${FEISHU_APP_SCOPES_TENANT-}"
73
+ AAMP_FEISHU_APP_SCOPES_USER_OVERRIDE="${FEISHU_APP_SCOPES_USER-}"
74
+ AAMP_FEISHU_USER_AUTH_REQUIRED_SCOPES_OVERRIDE="${FEISHU_USER_AUTH_REQUIRED_SCOPES-}"
75
+ AAMP_FEISHU_USER_AUTH_REQUESTED_SCOPES_OVERRIDE="${FEISHU_USER_AUTH_REQUESTED_SCOPES-}"
76
+ AAMP_FEISHU_USER_AUTH_EXCLUDES_OVERRIDE="${FEISHU_USER_AUTH_EXCLUDES-}"
77
+ FEISHU_APP_SCOPES_TENANT="$AAMP_FEISHU_APP_SCOPES_TENANT_OVERRIDE"
78
+ FEISHU_APP_SCOPES_USER="$AAMP_FEISHU_APP_SCOPES_USER_OVERRIDE"
58
79
  FEISHU_APP_EVENTS_TENANT="${FEISHU_APP_EVENTS_TENANT:-task.task.update_user_access_v2}"
59
80
  FEISHU_APP_EVENTS_USER="${FEISHU_APP_EVENTS_USER:-task.task.update_user_access_v2}"
60
- FEISHU_USER_AUTH_DOMAINS="${FEISHU_USER_AUTH_DOMAINS:-base,calendar,contact,docs,im,mail,mindnotes,minutes,note,sheets,slides,task,vc,wiki}"
61
- FEISHU_USER_AUTH_EXCLUDES="${FEISHU_USER_AUTH_EXCLUDES:-im:message.send_as_user,mail:user_mailbox.message:send,mail:user_mailbox.rule:read,mail:user_mailbox.folder:write,mail:user_mailbox.rule:write,mail:user_mailbox.message:modify,mail:user_mailbox.message:readonly,mail:user_mailbox.folder:read,mail:user_mailbox.mail_contact:write,mail:user_mailbox:readonly}"
62
- FEISHU_USER_AUTH_REQUIRED_SCOPES="${FEISHU_USER_AUTH_REQUIRED_SCOPES:-im:message im:message:readonly im:resource cardkit:card:write task:task task:comment task:task:readonly task:comment:readonly task:attachment:delete task:attachment:file:download task:attachment:read task:attachment:upload task:attachment:write task:comment:delete task:comment:read task:comment:write task:comment:writeonly task:task:delete task:task:read task:task:write task:task:writeonly task:tasklist:delete task:tasklist:read task:tasklist:write task:tasklist:writeonly search:docs:read search:message base:app:copy base:app:create base:app:read base:app:update base:block:create base:block:delete base:block:read base:block:update base:dashboard:create base:dashboard:delete base:dashboard:read base:dashboard:update base:field:create base:field:delete base:field:read base:field:update base:form:create base:form:delete base:form:read base:form:update base:history:read base:record:create base:record:delete base:record:read base:record:update base:role:create base:role:delete base:role:read base:role:update base:table:create base:table:delete base:table:read base:table:update base:view:read base:view:write_only base:workflow:create base:workflow:read base:workflow:update board:whiteboard:node:create board:whiteboard:node:read calendar:calendar.event:create calendar:calendar.event:delete calendar:calendar.event:read calendar:calendar.event:reply calendar:calendar.event:update calendar:calendar.free_busy:read calendar:calendar:create calendar:calendar:delete calendar:calendar:read calendar:calendar:update contact:user.base:readonly contact:user.basic_profile:readonly contact:user:search docs:document.media:download docs:document.media:upload docs:document:export docs:document:import docx:document:create docx:document:readonly docx:document:write_only drive:drive.metadata:readonly drive:file:download drive:file:upload im:chat.managers:write_only im:chat.members:read im:chat.members:write_only im:chat.moderation:read im:chat.nickname:read im:chat.nickname:write im:chat.user_setting:read im:chat.user_setting:write im:chat:read im:chat:update im:chat:create_by_user im:chat:moderation:write_only im:feed.flag:read im:feed.flag:write im:feed.shortcut:read im:feed.shortcut:write im:feed_group_v1:read im:feed_group_v1:write im:message.group_msg:get_as_user im:message.p2p_msg:get_as_user im:message.pins:read im:message.pins:write_only im:message.reactions:read im:message.reactions:write_only im:message:recall mail:event mail:user_mailbox.event.mail_address:read mail:user_mailbox.mail_contact:read mail:user_mailbox.message.address:read mail:user_mailbox.message.body:read mail:user_mailbox.message.subject:read mindnote:node:create mindnote:node:read minutes:minutes.artifacts:read minutes:minutes.basic:read minutes:minutes.media:export minutes:minutes.search:read minutes:minutes.upload:write minutes:minutes:readonly minutes:minutes:update profile:user_profile:read sheets:spreadsheet.meta:read sheets:spreadsheet.meta:write_only sheets:spreadsheet:create sheets:spreadsheet:read sheets:spreadsheet:write_only slides:presentation:create slides:presentation:read slides:presentation:update slides:presentation:write_only task:custom_field:read task:custom_field:write task:section:read task:section:write vc:meeting.bot.join:write vc:meeting.meetingevent:read vc:meeting.message:write vc:meeting.search:read vc:note:read vc:record:readonly wiki:member:create wiki:member:retrieve wiki:member:update wiki:node:copy wiki:node:create wiki:node:move wiki:node:read wiki:node:retrieve wiki:space:read wiki:space:retrieve wiki:space:write_only}"
63
- ACP_BRIDGE_PKG="${ACP_BRIDGE_PKG:-@zengxingyuan/aamp-acp-bridge@0.1.28-dev.20}"
81
+ FEISHU_USER_AUTH_EXCLUDES="$AAMP_FEISHU_USER_AUTH_EXCLUDES_OVERRIDE"
82
+ FEISHU_USER_AUTH_REQUIRED_SCOPES="$AAMP_FEISHU_USER_AUTH_REQUIRED_SCOPES_OVERRIDE"
83
+ FEISHU_USER_AUTH_REQUESTED_SCOPES="$AAMP_FEISHU_USER_AUTH_REQUESTED_SCOPES_OVERRIDE"
84
+ FEISHU_USER_AUTH_CORE_SCOPES="${FEISHU_USER_AUTH_CORE_SCOPES:-}"
85
+ FEISHU_USER_AUTH_OPTIONAL_SCOPES="${FEISHU_USER_AUTH_OPTIONAL_SCOPES:-}"
86
+ FEISHU_USER_AUTH_MODE="${FEISHU_USER_AUTH_MODE:-optional}"
87
+ FEISHU_TASK_PROFILE_DOMAINS="${FEISHU_TASK_PROFILE_DOMAINS:-task}"
88
+ AAMP_FEISHU_AUTH_STATE_DIR="${AAMP_FEISHU_AUTH_STATE_DIR:-$HOME/.aamp/feishu-bridge/auth-capabilities}"
89
+ AAMP_TASK_ALLOW_PACKAGE_OVERRIDES="${AAMP_TASK_ALLOW_PACKAGE_OVERRIDES:-false}"
90
+ if [ "$AAMP_TASK_INTERNAL" = "true" ] \
91
+ && [ "${AAMP_TASK_PACKAGE_OVERRIDES_RESOLVED:-false}" = "true" ]; then
92
+ AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG="${AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG:-}"
93
+ AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG="${AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG:-}"
94
+ AAMP_TASK_REQUESTED_AIME_ACP_PKG="${AAMP_TASK_REQUESTED_AIME_ACP_PKG:-}"
95
+ else
96
+ AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG="${ACP_BRIDGE_PKG:-${AAMP_TASK_ACP_BRIDGE_PKG:-}}"
97
+ AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG="${FEISHU_BRIDGE_PKG:-${AAMP_TASK_FEISHU_BRIDGE_PKG:-}}"
98
+ AAMP_TASK_REQUESTED_AIME_ACP_PKG="${AIME_ACP_PKG:-${AAMP_TASK_AIME_ACP_PKG:-}}"
99
+ fi
100
+ unset AAMP_TASK_PACKAGE_OVERRIDES_RESOLVED
101
+ unset ACP_BRIDGE_PKG AAMP_TASK_ACP_BRIDGE_PKG
102
+ unset FEISHU_BRIDGE_PKG AAMP_TASK_FEISHU_BRIDGE_PKG
103
+ unset AIME_ACP_PKG AIME_ACP_REGISTRY AAMP_TASK_AIME_ACP_PKG
104
+ ACP_BRIDGE_PKG="${ACP_BRIDGE_PKG:-@luckyterry/aamp-acp-bridge@0.1.29-dev.0}"
105
+ AIME_ACP_PKG="${AIME_ACP_PKG:-@tengchengwei/aime-acp@0.1.1-dev.1}"
106
+ AIME_ACP_REGISTRY="${AIME_ACP_REGISTRY:-https://bnpm.byted.org}"
64
107
  CLI_BRIDGE_PKG="${CLI_BRIDGE_PKG:-@zengxingyuan/aamp-cli-bridge@0.1.7-dev.14}"
65
- FEISHU_BRIDGE_PKG="${FEISHU_BRIDGE_PKG:-@zengxingyuan/aamp-feishu-bridge@0.1.51}"
108
+ FEISHU_BRIDGE_PKG="${FEISHU_BRIDGE_PKG:-@iluolyx/aamp-feishu-bridge@0.1.52-dev.5}"
109
+ AAMP_TASK_DEFAULT_ACP_BRIDGE_PKG="$ACP_BRIDGE_PKG"
110
+ AAMP_TASK_DEFAULT_FEISHU_BRIDGE_PKG="$FEISHU_BRIDGE_PKG"
111
+ AAMP_TASK_DEFAULT_AIME_ACP_PKG="$AIME_ACP_PKG"
66
112
  AAMP_TASK_AGENT_NAME="${AAMP_TASK_AGENT_NAME:-@larktask/aamp-feishu-task-agent}"
67
113
  AAMP_TASK_AGENT_LEGACY_NAME="${AAMP_TASK_AGENT_LEGACY_NAME:-@zengxingyuan/aamp-feishu-task-agent}"
68
- AAMP_TASK_AGENT_VERSION="0.1.0-dev.173"
114
+ AAMP_TASK_AGENT_VERSION="0.1.1-dev.2"
69
115
  AAMP_TASK_AGENT_CHANNEL="${AAMP_TASK_AGENT_CHANNEL:-dev}"
70
116
  AAMP_STALE_PROCESS_CLEANUP="${AAMP_STALE_PROCESS_CLEANUP:-false}"
71
117
  AAMP_STALE_PROCESS_SECONDS="${AAMP_STALE_PROCESS_SECONDS:-86400}"
@@ -85,8 +131,12 @@ CODEM_SERVICE_START_OUTPUT=""
85
131
  CODEM_AUTO_UPDATE_DONE="false"
86
132
  CODEM_FORCE_LOGIN_DONE="false"
87
133
  CODEM_PROVIDER_RECOVERY_DONE="false"
134
+ TRAE_CLI_BIN=""
135
+ TRAECODE_CLI_BIN=""
88
136
  PAIRING_URL=""
89
137
  ACP_AGENT_COMMAND=""
138
+ AGENT_PREPARE_CANCELLED="false"
139
+ AGENT_PREPARE_CANCEL_REASON=""
90
140
  STARTED_BRIDGE_PID=""
91
141
  ONE_CLICK_RUN_ID="$(date +%s)-$$"
92
142
  BOT_RESERVED="false"
@@ -115,6 +165,13 @@ sanitize_inherited_npm_exec_env() {
115
165
  done < <(env)
116
166
  }
117
167
 
168
+ remote_internal_helper() {
169
+ [ "$AAMP_TASK_INTERNAL" = "true" ] && {
170
+ [ "$AAMP_TASK_INTERNAL_EXECUTION_LOCATION" = "remote" ] \
171
+ || [ "$AGENT_EXECUTION_LOCATION" = "remote" ]
172
+ }
173
+ }
174
+
118
175
  usage() {
119
176
  cat <<'USAGE'
120
177
  Usage:
@@ -131,7 +188,8 @@ Running the standalone one-click script without a subcommand is the same as
131
188
  "feishu-task-agent install".
132
189
 
133
190
  Options:
134
- --agent codex|cursor Use this Agent for every new binding in the command.
191
+ --agent codex|cursor|coco|traex|traecli|workbuddy|workbuddy_ai|aime
192
+ Use this Agent for every new binding in the command.
135
193
  --aamp-host URL AAMP service URL. Default: https://meshmail.ai
136
194
  --debug Enable debug mode for bridge processes
137
195
  -h, --help Show this help
@@ -791,6 +849,7 @@ task_agent_global_install_is_complete() {
791
849
  [ -x "$NPM_GLOBAL_PREFIX/bin/aamp-logs" ] || return 1
792
850
  [ -r "$package_dir/bootstrap/aamp-feishu-task-agent-bootstrap.sh" ] || return 1
793
851
  [ -r "$package_dir/bin/feishu-task-agent-controller.mjs" ] || return 1
852
+ [ -r "$package_dir/bin/traecode-readiness.mjs" ] || return 1
794
853
  }
795
854
 
796
855
  task_agent_global_install_is_current() {
@@ -815,7 +874,7 @@ ensure_task_agent_global_install_locked() {
815
874
  fi
816
875
  remove_legacy_task_agent_global_install || return 1
817
876
  agent_detail "synchronizing task-agent npm package: current=${installed_version:-missing} expected=$expected_version"
818
- npm_install_global "$AAMP_TASK_AGENT_NAME@$expected_version" >>"$ONE_CLICK_LOG" 2>&1 || return 1
877
+ npm_install_global "$AAMP_TASK_AGENT_NAME@$expected_version" --force >>"$ONE_CLICK_LOG" 2>&1 || return 1
819
878
  task_agent_global_install_is_current "$expected_version"
820
879
  }
821
880
 
@@ -829,12 +888,16 @@ ensure_task_agent_global_install() {
829
888
 
830
889
  short_command_is_current() {
831
890
  local target="$1"
891
+ local source_file="${2:-}"
832
892
  local installed_name installed_version
833
893
  [ -x "$target" ] || return 1
834
894
  installed_name="$(script_file_task_agent_name "$target" || true)"
835
895
  installed_version="$(script_file_task_agent_version "$target" || true)"
836
896
  [ "$installed_name" = "$AAMP_TASK_AGENT_NAME" ] || return 1
837
- [ "$installed_version" = "$AAMP_TASK_AGENT_VERSION" ]
897
+ [ "$installed_version" = "$AAMP_TASK_AGENT_VERSION" ] || return 1
898
+ if [ -n "$source_file" ] && [ -r "$source_file" ]; then
899
+ cmp -s "$source_file" "$target"
900
+ fi
838
901
  }
839
902
 
840
903
  write_task_update_cache() {
@@ -912,13 +975,13 @@ install_short_command_current() {
912
975
  local target="$1"
913
976
  local source_file="${BASH_SOURCE[0]:-}"
914
977
  local tmp="${target}.tmp.$$"
915
- if short_command_is_current "$target"; then
916
- agent_detail "short command already current: $target"
917
- return 0
918
- fi
919
978
  case "$source_file" in
920
979
  ""|/dev/fd/*|/private/dev/fd/*|/proc/*) source_file="" ;;
921
980
  esac
981
+ if short_command_is_current "$target" "$source_file"; then
982
+ agent_detail "short command already current: $target"
983
+ return 0
984
+ fi
922
985
  if [ -n "$source_file" ] && [ -f "$source_file" ] && [ -r "$source_file" ]; then
923
986
  mkdir -p "$(dirname "$target")"
924
987
  cp "$source_file" "$tmp" 2>>"$ONE_CLICK_LOG" || return 1
@@ -949,6 +1012,7 @@ ensure_selected_agent_for_start() {
949
1012
  select_agent_interactively
950
1013
  fi
951
1014
  validate_agent_name "$AGENT"
1015
+ ensure_agent_selection_available "$AGENT"
952
1016
  }
953
1017
 
954
1018
  resolve_latest_task_agent_version() {
@@ -1033,12 +1097,109 @@ run_task_agent_update_command() {
1033
1097
  install_aamp_logs_bin "$installed_version" || agent_fail "failed to synchronize aamp-logs $installed_version"
1034
1098
  printf '当前版本:%s\n' "$installed_version"
1035
1099
  }
1036
- npm_install_global() {
1100
+ classify_remote_npm_cache_error() {
1101
+ node -e '
1102
+ let carry = "";
1103
+ let lineSawEnoent = false;
1104
+ let lineSawTarballData = false;
1105
+ let matched = false;
1106
+ const scan = (part, endOfLine) => {
1107
+ const text = (carry + part).toLowerCase();
1108
+ const sawEnoent = lineSawEnoent || text.includes("enoent");
1109
+ const sawTarballData = lineSawTarballData || text.includes("tarball data");
1110
+ if (text.includes("invalid response body")
1111
+ || text.includes("_cacache")
1112
+ || (sawEnoent && text.includes("content-v2"))
1113
+ || (sawTarballData && text.includes("corrupted"))
1114
+ || text.includes("tar_bad_archive")
1115
+ || text.includes("zlib: unexpected end of file")) matched = true;
1116
+ lineSawEnoent = sawEnoent;
1117
+ lineSawTarballData = sawTarballData;
1118
+ carry = text.slice(-64);
1119
+ if (endOfLine) {
1120
+ carry = "";
1121
+ lineSawEnoent = false;
1122
+ lineSawTarballData = false;
1123
+ }
1124
+ };
1125
+ process.stdin.on("data", (chunk) => {
1126
+ const parts = chunk.toString("latin1").split(/\r?\n/);
1127
+ parts.forEach((part, index) => scan(part, index < parts.length - 1));
1128
+ });
1129
+ process.stdin.on("end", () => { process.exitCode = matched ? 0 : 1; });
1130
+ process.stdin.on("error", () => { process.exitCode = 2; });
1131
+ '
1132
+ }
1133
+
1134
+ REMOTE_NPM_INSTALL_STATUS=1
1135
+ REMOTE_NPM_CACHE_ERROR=false
1136
+ run_remote_npm_install_attempt() {
1137
+ local registry="$1"
1138
+ shift
1139
+ local pipeline_status=()
1140
+ sanitize_inherited_npm_exec_env
1141
+ set +e
1142
+ "$NPM_BIN" install -g \
1143
+ --registry "$registry" \
1144
+ --cache "$NPM_CACHE_DIR" \
1145
+ --prefix "$NPM_GLOBAL_PREFIX" \
1146
+ "$@" 2>&1 | classify_remote_npm_cache_error
1147
+ pipeline_status=("${PIPESTATUS[@]}")
1148
+ set -e
1149
+ REMOTE_NPM_INSTALL_STATUS="${pipeline_status[0]:-1}"
1150
+ REMOTE_NPM_CACHE_ERROR=false
1151
+ if [ "${pipeline_status[1]:-2}" -eq 0 ]; then
1152
+ REMOTE_NPM_CACHE_ERROR=true
1153
+ fi
1154
+ return 0
1155
+ }
1156
+
1157
+ npm_install_global_from_registry_remote() {
1158
+ local registry="$1"
1159
+ shift
1160
+ finish_remote_npm_install() {
1161
+ trap - EXIT HUP INT TERM
1162
+ unset -f finish_remote_npm_install
1163
+ }
1164
+ trap finish_remote_npm_install EXIT
1165
+ trap 'exit 129' HUP
1166
+ trap 'exit 130' INT
1167
+ trap 'exit 143' TERM
1168
+ run_remote_npm_install_attempt "$registry" "$@"
1169
+ if [ "$REMOTE_NPM_INSTALL_STATUS" -eq 0 ]; then
1170
+ hash -r 2>/dev/null || true
1171
+ finish_remote_npm_install
1172
+ return 0
1173
+ fi
1174
+
1175
+ if [ "$REMOTE_NPM_CACHE_ERROR" != "true" ]; then
1176
+ finish_remote_npm_install
1177
+ return 1
1178
+ fi
1179
+
1180
+ reset_npm_cache_for_retry
1181
+ run_remote_npm_install_attempt "$registry" "$@"
1182
+ if [ "$REMOTE_NPM_INSTALL_STATUS" -ne 0 ]; then
1183
+ finish_remote_npm_install
1184
+ return 1
1185
+ fi
1186
+ hash -r 2>/dev/null || true
1187
+ finish_remote_npm_install
1188
+ }
1189
+
1190
+ npm_install_global_from_registry() {
1191
+ if remote_internal_helper; then
1192
+ npm_install_global_from_registry_remote "$@"
1193
+ return $?
1194
+ fi
1195
+
1196
+ local registry="$1"
1197
+ shift
1037
1198
  local npm_log
1038
1199
  npm_log="$(mktemp "${TMPDIR:-/tmp}/aamp-npm-install.XXXXXX")"
1039
1200
  sanitize_inherited_npm_exec_env
1040
1201
  if "$NPM_BIN" install -g \
1041
- --registry "$NPM_REGISTRY" \
1202
+ --registry "$registry" \
1042
1203
  --cache "$NPM_CACHE_DIR" \
1043
1204
  --prefix "$NPM_GLOBAL_PREFIX" \
1044
1205
  "$@" >"$npm_log" 2>&1; then
@@ -1056,7 +1217,7 @@ npm_install_global() {
1056
1217
  npm_log="$(mktemp "${TMPDIR:-/tmp}/aamp-npm-install-retry.XXXXXX")"
1057
1218
  sanitize_inherited_npm_exec_env
1058
1219
  "$NPM_BIN" install -g \
1059
- --registry "$NPM_REGISTRY" \
1220
+ --registry "$registry" \
1060
1221
  --cache "$NPM_CACHE_DIR" \
1061
1222
  --prefix "$NPM_GLOBAL_PREFIX" \
1062
1223
  "$@" >"$npm_log" 2>&1 || {
@@ -1067,6 +1228,10 @@ npm_install_global() {
1067
1228
  hash -r 2>/dev/null || true
1068
1229
  }
1069
1230
 
1231
+ npm_install_global() {
1232
+ npm_install_global_from_registry "$NPM_REGISTRY" "$@"
1233
+ }
1234
+
1070
1235
  npx_package() {
1071
1236
  local npm_log
1072
1237
  local status
@@ -1176,13 +1341,32 @@ npm_install_register_helper() {
1176
1341
  }
1177
1342
  }
1178
1343
 
1344
+ aime_internal_network_reachable() {
1345
+ command -v ping >/dev/null 2>&1 || return 1
1346
+ if is_macos; then
1347
+ ping -c 1 -W 1000 aime.bytedance.net >/dev/null 2>&1
1348
+ else
1349
+ ping -c 1 -W 1 aime.bytedance.net >/dev/null 2>&1
1350
+ fi
1351
+ }
1352
+
1179
1353
  validate_agent_name() {
1180
1354
  case "$1" in
1181
- codex|cursor) ;;
1182
- *) agent_fail "--agent must be codex or cursor" ;;
1355
+ codex|cursor|coco|traex|traecli|workbuddy|workbuddy_ai|aime) ;;
1356
+ *) agent_fail "--agent must be codex, cursor, coco, traex, traecli, workbuddy, workbuddy_ai, or aime" ;;
1183
1357
  esac
1184
1358
  }
1185
1359
 
1360
+ ensure_agent_selection_available() {
1361
+ [ "$1" = "aime" ] || return 0
1362
+ aime_internal_network_reachable \
1363
+ || agent_fail "AIME 仅在公司内网可用;当前无法 ping 通 aime.bytedance.net。"
1364
+ }
1365
+
1366
+ agent_display_name() {
1367
+ printf '%s' "$1"
1368
+ }
1369
+
1186
1370
  read_tty_line() {
1187
1371
  local prompt="$1"
1188
1372
  local value
@@ -1202,6 +1386,24 @@ agent_cli_detected() {
1202
1386
  cursor)
1203
1387
  find_cursor_agent_cli >/dev/null 2>&1
1204
1388
  ;;
1389
+ traex)
1390
+ find_traex_cli >/dev/null 2>&1
1391
+ ;;
1392
+ coco)
1393
+ find_legacy_trae_cli >/dev/null 2>&1
1394
+ ;;
1395
+ traecli)
1396
+ find_traecode_cli >/dev/null 2>&1
1397
+ ;;
1398
+ workbuddy)
1399
+ find_workbuddy_cli >/dev/null 2>&1
1400
+ ;;
1401
+ workbuddy_ai)
1402
+ find_workbuddy_ai_cli >/dev/null 2>&1
1403
+ ;;
1404
+ aime)
1405
+ aime_internal_network_reachable
1406
+ ;;
1205
1407
  *)
1206
1408
  return 1
1207
1409
  ;;
@@ -1216,9 +1418,25 @@ discover_interactive_agents() {
1216
1418
  if agent_cli_detected cursor; then
1217
1419
  DETECTED_AGENTS+=("cursor")
1218
1420
  fi
1421
+ if agent_cli_detected traex; then
1422
+ DETECTED_AGENTS+=("traex")
1423
+ elif agent_cli_detected coco; then
1424
+ DETECTED_AGENTS+=("coco")
1425
+ elif agent_cli_detected traecli; then
1426
+ DETECTED_AGENTS+=("traecli")
1427
+ fi
1428
+ if agent_cli_detected workbuddy; then
1429
+ DETECTED_AGENTS+=("workbuddy")
1430
+ fi
1431
+ if agent_cli_detected workbuddy_ai; then
1432
+ DETECTED_AGENTS+=("workbuddy_ai")
1433
+ fi
1434
+ if agent_cli_detected aime; then
1435
+ DETECTED_AGENTS+=("aime")
1436
+ fi
1219
1437
 
1220
1438
  if [ "${#DETECTED_AGENTS[@]}" -eq 0 ]; then
1221
- agent_fail "暂未检测到本地智能体。请先安装并登录 Codex 或 Cursor CLI 后重试。"
1439
+ agent_fail "暂未检测到本地智能体。请先安装 Codex、Cursor、Trae CLI、WorkBuddy WorkBuddy AI 后重试。"
1222
1440
  fi
1223
1441
  }
1224
1442
 
@@ -1237,9 +1455,9 @@ render_agent_menu() {
1237
1455
  for index in "${!DETECTED_AGENTS[@]}"; do
1238
1456
  printf '\033[2K\r' >&3
1239
1457
  if [ "$index" -eq "$selected" ]; then
1240
- printf ' > %s\n' "${DETECTED_AGENTS[$index]}" >&3
1458
+ printf ' > %s\n' "$(agent_display_name "${DETECTED_AGENTS[$index]}")" >&3
1241
1459
  else
1242
- printf ' %s\n' "${DETECTED_AGENTS[$index]}" >&3
1460
+ printf ' %s\n' "$(agent_display_name "${DETECTED_AGENTS[$index]}")" >&3
1243
1461
  fi
1244
1462
  done
1245
1463
  printf '\033[2K\r使用 ↑/↓ 选择,回车确认。也可按数字键或 j/k。\n' >&3
@@ -1253,7 +1471,7 @@ select_agent_interactively() {
1253
1471
  discover_interactive_agents
1254
1472
 
1255
1473
  if ! exec 3<>/dev/tty; then
1256
- agent_fail "missing --agent and no interactive terminal is available; pass --agent codex|cursor"
1474
+ agent_fail "missing --agent and no interactive terminal is available; pass --agent codex|cursor|coco|traex|traecli|workbuddy|workbuddy_ai|aime"
1257
1475
  fi
1258
1476
 
1259
1477
  tty_state="$(stty -g <&3)"
@@ -1439,7 +1657,7 @@ parse_args() {
1439
1657
  fi
1440
1658
 
1441
1659
  case "${1:-}" in
1442
- install|start|list|add|remove|update|help|__discover-agents|__register-binding|__prepare-agent|__ensure-profile)
1660
+ install|start|list|add|remove|update|help|__discover-agents|__register-binding|__prepare-agent|__probe-profile|__ensure-profile)
1443
1661
  AAMP_TASK_ACTION="$1"
1444
1662
  shift
1445
1663
  ;;
@@ -1794,7 +2012,7 @@ save_bot_config() {
1794
2012
  local profile="$3"
1795
2013
  local app_secret="${4:-}"
1796
2014
  mkdir -p "$(dirname "$BOT_CONFIG_FILE")"
1797
- BOT_CONFIG_FILE="$BOT_CONFIG_FILE" BOT_NAME="$bot_name" BOT_APP_ID="$app_id" BOT_PROFILE="$profile" BOT_APP_SECRET="$app_secret" FEISHU_USER_AUTH_DOMAINS="$FEISHU_USER_AUTH_DOMAINS" node -e '
2015
+ BOT_CONFIG_FILE="$BOT_CONFIG_FILE" BOT_NAME="$bot_name" BOT_APP_ID="$app_id" BOT_PROFILE="$profile" BOT_APP_SECRET="$app_secret" FEISHU_TASK_PROFILE_DOMAINS="$FEISHU_TASK_PROFILE_DOMAINS" FEISHU_SCOPE_MANIFEST_VERSION="$FEISHU_SCOPE_MANIFEST_VERSION" FEISHU_USER_AUTH_MODE="$FEISHU_USER_AUTH_MODE" node -e '
1798
2016
  const fs = require("fs");
1799
2017
  const file = process.env.BOT_CONFIG_FILE;
1800
2018
  const appSecret = String(process.env.BOT_APP_SECRET || "").trim();
@@ -1805,7 +2023,9 @@ const next = {
1805
2023
  profile: process.env.BOT_PROFILE,
1806
2024
  auth_mode: "lark-cli",
1807
2025
  capabilities: ["im", "task"],
1808
- domains: String(process.env.FEISHU_USER_AUTH_DOMAINS || "").split(",").map((item) => item.trim()).filter(Boolean),
2026
+ domains: String(process.env.FEISHU_TASK_PROFILE_DOMAINS || "task").split(",").map((item) => item.trim()).filter(Boolean),
2027
+ scope_manifest_version: Number(process.env.FEISHU_SCOPE_MANIFEST_VERSION || 0),
2028
+ user_auth_mode: process.env.FEISHU_USER_AUTH_MODE || "optional",
1809
2029
  updated_at: new Date().toISOString(),
1810
2030
  };
1811
2031
  let parsed = { version: 1, profiles: [] };
@@ -2038,15 +2258,65 @@ ensure_lark_cli_min_version() {
2038
2258
  select_lark_cli_bin
2039
2259
  }
2040
2260
 
2261
+ feishu_scope_manifest_json() {
2262
+ cat <<'JSON'
2263
+ {"version":2,"app":{"tenant":["im:message","im:message:send_as_bot","im:message:readonly","im:resource","cardkit:card:write","task:task","task:comment","task:task:readonly","task:comment:readonly","task:attachment:read","task:attachment:write","task:comment:read","task:comment:write","task:task:read","task:task:write","task:tasklist:read","task:tasklist:write","task:custom_field:read","task:custom_field:write","task:section:read","task:section:write"],"user":["im:message","im:message:readonly","im:resource","cardkit:card:write","task:task","task:comment","task:task:readonly","task:comment:readonly","task:attachment:read","task:attachment:write","task:comment:read","task:comment:write","task:task:read","task:task:write","task:tasklist:read","task:tasklist:write","task:custom_field:read","task:custom_field:write","task:section:read","task:section:write"]},"userAuth":{"core":[],"optional":["task:task","task:comment","task:task:readonly","task:comment:readonly","task:attachment:read","task:attachment:write","task:comment:read","task:comment:write","task:task:read","task:task:write","task:tasklist:read","task:tasklist:write","task:custom_field:read","task:custom_field:write","task:section:read","task:section:write"]}}
2264
+ JSON
2265
+ }
2266
+
2267
+ initialize_feishu_scope_manifest() {
2268
+ local manifest_values=()
2269
+ local value
2270
+
2271
+ while IFS= read -r value; do
2272
+ manifest_values+=("$value")
2273
+ done < <(feishu_scope_manifest_json | node -e '
2274
+ const fs = require("fs");
2275
+ const manifest = JSON.parse(fs.readFileSync(0, "utf8"));
2276
+ console.log(String(manifest.version));
2277
+ console.log(manifest.app.tenant.join(","));
2278
+ console.log(manifest.app.user.join(","));
2279
+ console.log(manifest.userAuth.core.join(" "));
2280
+ console.log(manifest.userAuth.optional.join(" "));
2281
+ ')
2282
+
2283
+ [ "${#manifest_values[@]}" -eq 5 ] || agent_fail "invalid embedded Feishu scope manifest"
2284
+ FEISHU_SCOPE_MANIFEST_VERSION="${FEISHU_SCOPE_MANIFEST_VERSION:-${manifest_values[0]}}"
2285
+ FEISHU_APP_SCOPES_TENANT="${FEISHU_APP_SCOPES_TENANT:-${manifest_values[1]}}"
2286
+ FEISHU_APP_SCOPES_USER="${FEISHU_APP_SCOPES_USER:-${manifest_values[2]}}"
2287
+ FEISHU_USER_AUTH_CORE_SCOPES="${FEISHU_USER_AUTH_CORE_SCOPES:-${manifest_values[3]}}"
2288
+ FEISHU_USER_AUTH_OPTIONAL_SCOPES="${FEISHU_USER_AUTH_OPTIONAL_SCOPES:-${manifest_values[4]}}"
2289
+ if [ -n "${FEISHU_USER_AUTH_REQUIRED_SCOPES:-}" ]; then
2290
+ FEISHU_USER_AUTH_CORE_SCOPES="$FEISHU_USER_AUTH_REQUIRED_SCOPES"
2291
+ else
2292
+ FEISHU_USER_AUTH_REQUIRED_SCOPES="$FEISHU_USER_AUTH_CORE_SCOPES"
2293
+ fi
2294
+ if [ -z "${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}" ]; then
2295
+ if [ -n "${AAMP_FEISHU_USER_AUTH_REQUIRED_SCOPES_OVERRIDE:-}" ]; then
2296
+ FEISHU_USER_AUTH_REQUESTED_SCOPES="$FEISHU_USER_AUTH_REQUIRED_SCOPES"
2297
+ else
2298
+ FEISHU_USER_AUTH_REQUESTED_SCOPES="${FEISHU_USER_AUTH_CORE_SCOPES} ${FEISHU_USER_AUTH_OPTIONAL_SCOPES}"
2299
+ fi
2300
+ fi
2301
+ export FEISHU_SCOPE_MANIFEST_VERSION
2302
+ export FEISHU_APP_SCOPES_TENANT FEISHU_APP_SCOPES_USER
2303
+ export FEISHU_USER_AUTH_CORE_SCOPES FEISHU_USER_AUTH_OPTIONAL_SCOPES
2304
+ export FEISHU_USER_AUTH_REQUIRED_SCOPES FEISHU_USER_AUTH_REQUESTED_SCOPES
2305
+ }
2306
+
2041
2307
  lark_cli_user_auth_satisfied() {
2042
2308
  local profile="$1"
2309
+ local required_scopes="${2:-${FEISHU_USER_AUTH_REQUIRED_SCOPES:-${FEISHU_USER_AUTH_CORE_SCOPES:-}}}"
2043
2310
  local status_json auth_check
2044
2311
  LARK_CLI_CMD="${LARK_CLI_CMD:-lark-cli}"
2045
2312
 
2313
+ if [ -z "${required_scopes//[[:space:],]/}" ]; then
2314
+ return 0
2315
+ fi
2046
2316
  status_json="$("$LARK_CLI_CMD" --profile "$profile" auth status --json 2>/dev/null)" || return 1
2047
2317
 
2048
2318
  set +e
2049
- auth_check="$(AUTH_STATUS_JSON="$status_json" FEISHU_USER_AUTH_REQUIRED_SCOPES="$FEISHU_USER_AUTH_REQUIRED_SCOPES" FEISHU_USER_AUTH_EXCLUDES="$FEISHU_USER_AUTH_EXCLUDES" node -e '
2319
+ auth_check="$(AUTH_STATUS_JSON="$status_json" FEISHU_USER_AUTH_REQUIRED_SCOPES="$required_scopes" FEISHU_USER_AUTH_EXCLUDES="$FEISHU_USER_AUTH_EXCLUDES" node -e '
2050
2320
  function parseJsonOutput(value, label) {
2051
2321
  const raw = String(value || "");
2052
2322
  const start = raw.indexOf("{");
@@ -2087,10 +2357,76 @@ console.log("ok");
2087
2357
  return 1
2088
2358
  }
2089
2359
 
2360
+ write_lark_cli_auth_capability_snapshot() {
2361
+ local profile="$1"
2362
+ local status_json="{}"
2363
+ local state_dir="$AAMP_FEISHU_AUTH_STATE_DIR"
2364
+ local safe_profile state_file temp_file
2365
+
2366
+ set +e
2367
+ status_json="$("$LARK_CLI_CMD" --profile "$profile" auth status --json 2>/dev/null)"
2368
+ local status_code=$?
2369
+ set -e
2370
+ if [ "$status_code" -ne 0 ] || [ -z "$status_json" ]; then
2371
+ status_json="{}"
2372
+ fi
2373
+
2374
+ mkdir -p "$state_dir" || return 1
2375
+ safe_profile="$(PROFILE="$profile" node -e 'process.stdout.write(String(process.env.PROFILE || "profile").replace(/[^A-Za-z0-9._-]/g, "_"))')" || return 1
2376
+ state_file="$state_dir/$safe_profile.json"
2377
+ temp_file="$(mktemp "$state_dir/.auth-capabilities.XXXXXX")" || return 1
2378
+ if ! AUTH_STATUS_JSON="$status_json" \
2379
+ AUTH_PROFILE="$profile" \
2380
+ FEISHU_SCOPE_MANIFEST_VERSION="$FEISHU_SCOPE_MANIFEST_VERSION" \
2381
+ FEISHU_USER_AUTH_CORE_SCOPES="$FEISHU_USER_AUTH_CORE_SCOPES" \
2382
+ FEISHU_USER_AUTH_OPTIONAL_SCOPES="$FEISHU_USER_AUTH_OPTIONAL_SCOPES" \
2383
+ node -e '
2384
+ const raw = String(process.env.AUTH_STATUS_JSON || "{}");
2385
+ const start = raw.indexOf("{");
2386
+ let status = {};
2387
+ try { status = JSON.parse(start >= 0 ? raw.slice(start) : "{}"); } catch {}
2388
+ const user = status?.identities?.user;
2389
+ const usable = Boolean(user?.available) && new Set(["valid", "needs_refresh"]).has(user?.tokenStatus);
2390
+ const split = (value) => [...new Set(String(value || "").split(/[\s,]+/).map((item) => item.trim()).filter(Boolean))];
2391
+ const granted = new Set(split(user?.scope));
2392
+ const core = split(process.env.FEISHU_USER_AUTH_CORE_SCOPES);
2393
+ const optional = split(process.env.FEISHU_USER_AUTH_OPTIONAL_SCOPES);
2394
+ const missingCoreScopes = core.filter((scope) => !granted.has(scope));
2395
+ const missingOptionalScopes = optional.filter((scope) => !granted.has(scope));
2396
+ process.stdout.write(JSON.stringify({
2397
+ schemaVersion: 1,
2398
+ manifestVersion: Number(process.env.FEISHU_SCOPE_MANIFEST_VERSION || 0),
2399
+ profile: process.env.AUTH_PROFILE,
2400
+ tokenStatus: user?.tokenStatus || "missing",
2401
+ grantedScopes: [...granted],
2402
+ missingCoreScopes,
2403
+ missingOptionalScopes,
2404
+ capabilities: { task_user: usable && missingOptionalScopes.length === 0 },
2405
+ checkedAt: new Date().toISOString(),
2406
+ }, null, 2));
2407
+ ' >"$temp_file"; then
2408
+ rm -f "$temp_file"
2409
+ return 1
2410
+ fi
2411
+ if ! mv "$temp_file" "$state_file"; then
2412
+ rm -f "$temp_file"
2413
+ return 1
2414
+ fi
2415
+ chmod 600 "$state_file" 2>/dev/null || true
2416
+ }
2417
+
2418
+ persist_lark_cli_auth_capability_snapshot() {
2419
+ local profile="$1"
2420
+ if ! write_lark_cli_auth_capability_snapshot "$profile"; then
2421
+ agent_log "warning: unable to persist optional user capability snapshot for profile: $profile"
2422
+ fi
2423
+ }
2424
+
2090
2425
  normalize_lark_cli_auth_excludes() {
2091
- EXCLUDES="$1" node -e '
2426
+ EXCLUDES="$1" REQUESTED_SCOPES="${2:-${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}}" node -e '
2092
2427
  const excludes = String(process.env.EXCLUDES || "").split(",").map((item) => item.trim()).filter(Boolean);
2093
- console.log([...new Set(excludes)].join(","));
2428
+ const requested = new Set(String(process.env.REQUESTED_SCOPES || "").split(/[\s,]+/).map((item) => item.trim()).filter(Boolean));
2429
+ console.log([...new Set(excludes)].filter((scope) => requested.has(scope)).join(","));
2094
2430
  '
2095
2431
  }
2096
2432
 
@@ -2237,12 +2573,14 @@ run_lark_cli_auth_login_with_browser_open() {
2237
2573
  run_lark_cli_auth_login() {
2238
2574
  local profile="$1"
2239
2575
  local auth_excludes="$2"
2576
+ local requested_scopes="${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}"
2240
2577
 
2241
- auth_excludes="$(normalize_lark_cli_auth_excludes "$auth_excludes")"
2578
+ [ -n "${requested_scopes//[[:space:],]/}" ] || return 0
2579
+ auth_excludes="$(normalize_lark_cli_auth_excludes "$auth_excludes" "$requested_scopes")"
2242
2580
  if [ -z "$auth_excludes" ]; then
2243
- run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --domain "$FEISHU_USER_AUTH_DOMAINS" --scope "$FEISHU_USER_AUTH_REQUIRED_SCOPES"
2581
+ run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --scope "$requested_scopes"
2244
2582
  else
2245
- run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --domain "$FEISHU_USER_AUTH_DOMAINS" --scope "$FEISHU_USER_AUTH_REQUIRED_SCOPES" --exclude "$auth_excludes"
2583
+ run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --scope "$requested_scopes" --exclude "$auth_excludes"
2246
2584
  fi
2247
2585
  }
2248
2586
 
@@ -2251,11 +2589,43 @@ ensure_lark_cli_profile() {
2251
2589
  with_lark_cli_config_lock ensure_lark_cli_profile_locked "$@"
2252
2590
  }
2253
2591
 
2592
+ validate_feishu_user_auth_mode() {
2593
+ case "$FEISHU_USER_AUTH_MODE" in
2594
+ optional|required|disabled) ;;
2595
+ *) agent_fail "FEISHU_USER_AUTH_MODE must be optional, required, or disabled" ;;
2596
+ esac
2597
+ }
2598
+
2599
+ probe_lark_cli_profile_locked() {
2600
+ local profile="$1"
2601
+ local required_scopes
2602
+
2603
+ initialize_feishu_scope_manifest
2604
+ validate_feishu_user_auth_mode
2605
+ "$LARK_CLI_CMD" profile list 2>/dev/null | grep -F "\"$profile\"" >/dev/null 2>&1 \
2606
+ || return 1
2607
+ if [ "$FEISHU_USER_AUTH_MODE" = "disabled" ]; then
2608
+ persist_lark_cli_auth_capability_snapshot "$profile"
2609
+ return 0
2610
+ fi
2611
+ if [ "$FEISHU_USER_AUTH_MODE" = "required" ]; then
2612
+ required_scopes="$FEISHU_USER_AUTH_REQUESTED_SCOPES"
2613
+ else
2614
+ required_scopes="$FEISHU_USER_AUTH_CORE_SCOPES"
2615
+ fi
2616
+ lark_cli_user_auth_satisfied "$profile" "$required_scopes" || return 1
2617
+ persist_lark_cli_auth_capability_snapshot "$profile"
2618
+ }
2619
+
2254
2620
  ensure_lark_cli_profile_locked() {
2255
2621
  local app_id="$1"
2256
2622
  local app_secret="$2"
2257
2623
  local profile="$3"
2258
2624
  local auth_excludes
2625
+ local required_scopes
2626
+
2627
+ initialize_feishu_scope_manifest
2628
+ validate_feishu_user_auth_mode
2259
2629
 
2260
2630
  if "$LARK_CLI_CMD" profile list 2>/dev/null | grep -F "\"$profile\"" >/dev/null 2>&1; then
2261
2631
  agent_detail "lark-cli profile already exists: $profile"
@@ -2268,13 +2638,38 @@ ensure_lark_cli_profile_locked() {
2268
2638
  --app-secret-stdin
2269
2639
  fi
2270
2640
 
2271
- agent_detail "ensuring lark-cli user auth domains for profile: $profile"
2641
+ if [ "$FEISHU_USER_AUTH_MODE" = "disabled" ]; then
2642
+ persist_lark_cli_auth_capability_snapshot "$profile"
2643
+ agent_log "Task bridge ready; optional user capabilities disabled for profile: $profile"
2644
+ return 0
2645
+ fi
2646
+
2272
2647
  auth_excludes="$FEISHU_USER_AUTH_EXCLUDES"
2273
- if lark_cli_user_auth_satisfied "$profile"; then
2274
- agent_detail "lark-cli user auth already has required scopes for profile: $profile"
2275
- else
2648
+ if [ "$FEISHU_USER_AUTH_MODE" = "required" ]; then
2649
+ required_scopes="$FEISHU_USER_AUTH_REQUESTED_SCOPES"
2650
+ agent_detail "ensuring explicit lark-cli user auth scopes for profile: $profile"
2651
+ if ! lark_cli_user_auth_satisfied "$profile" "$required_scopes"; then
2652
+ run_lark_cli_auth_login "$profile" "$auth_excludes"
2653
+ lark_cli_user_auth_satisfied "$profile" "$required_scopes" \
2654
+ || agent_fail "lark-cli user authorization did not grant the required explicit scopes"
2655
+ fi
2656
+ persist_lark_cli_auth_capability_snapshot "$profile"
2657
+ agent_detail "lark-cli user auth has the requested scopes for profile: $profile"
2658
+ return 0
2659
+ fi
2660
+
2661
+ if ! lark_cli_user_auth_satisfied "$profile" "$FEISHU_USER_AUTH_CORE_SCOPES"; then
2662
+ agent_detail "ensuring explicitly configured lark-cli core scopes for profile: $profile"
2276
2663
  run_lark_cli_auth_login "$profile" "$auth_excludes"
2664
+ lark_cli_user_auth_satisfied "$profile" "$FEISHU_USER_AUTH_CORE_SCOPES" \
2665
+ || agent_fail "lark-cli user authorization did not grant the configured core scopes"
2666
+ fi
2667
+ if lark_cli_user_auth_satisfied "$profile" "$FEISHU_USER_AUTH_OPTIONAL_SCOPES"; then
2668
+ agent_detail "lark-cli optional user capabilities are available for profile: $profile"
2669
+ else
2670
+ agent_log "Task bridge ready; optional user capabilities unavailable for profile: $profile"
2277
2671
  fi
2672
+ persist_lark_cli_auth_capability_snapshot "$profile"
2278
2673
  }
2279
2674
 
2280
2675
  forget_current_bot_after_feishu_start_failure() {
@@ -2375,6 +2770,8 @@ register_feishu_app() {
2375
2770
  local default_name
2376
2771
  local bot_name
2377
2772
 
2773
+ initialize_feishu_scope_manifest
2774
+
2378
2775
  workdir="$(mktemp -d "${TMPDIR:-/tmp}/aamp-register-feishu-app.XXXXXX")"
2379
2776
  register_script="$workdir/register-app.mjs"
2380
2777
  if [ -n "$AAMP_RUN_LOG_DIR" ]; then
@@ -2522,8 +2919,13 @@ NODE
2522
2919
 
2523
2920
  bot_name="${bot_name:-$default_name}"
2524
2921
  BOT_NAME="$bot_name"
2525
- LARK_CLI_PROFILE="$(task_profile_name_for_app_id "$APP_ID")"
2526
- ensure_lark_cli_profile "$APP_ID" "$APP_SECRET" "$LARK_CLI_PROFILE"
2922
+ if [ "$AGENT_EXECUTION_LOCATION" = "remote" ]; then
2923
+ LARK_CLI_PROFILE=""
2924
+ else
2925
+ LARK_CLI_PROFILE="$(task_profile_name_for_app_id "$APP_ID")"
2926
+ source_lark_env
2927
+ ensure_lark_cli_profile "$APP_ID" "$APP_SECRET" "$LARK_CLI_PROFILE"
2928
+ fi
2527
2929
  if [ "$AAMP_TASK_INTERNAL" = "true" ]; then
2528
2930
  agent_detail "Feishu Bot 已授权:$BOT_NAME ($APP_ID)"
2529
2931
  return 0
@@ -2677,6 +3079,338 @@ find_cursor_agent_cli() {
2677
3079
  return 1
2678
3080
  }
2679
3081
 
3082
+ resolve_trae_cli_candidate() {
3083
+ local candidate
3084
+ candidate="$1"
3085
+ [ -n "$candidate" ] || return 1
3086
+ if command -v "$candidate" >/dev/null 2>&1; then
3087
+ command -v "$candidate"
3088
+ return 0
3089
+ fi
3090
+ if [ -x "$candidate" ]; then
3091
+ printf '%s\n' "$candidate"
3092
+ return 0
3093
+ fi
3094
+ return 1
3095
+ }
3096
+
3097
+ find_traex_cli_candidates() {
3098
+ local candidate resolved emitted=""
3099
+ local candidates=()
3100
+ if [ -n "$TRAE_CLI_BIN" ] && [ "$(basename "$TRAE_CLI_BIN")" = "traex" ]; then candidates+=("$TRAE_CLI_BIN"); fi
3101
+ if [ -n "$AAMP_TRAE_CLI_BIN" ] && [ "$(basename "$AAMP_TRAE_CLI_BIN")" = "traex" ]; then candidates+=("$AAMP_TRAE_CLI_BIN"); fi
3102
+ candidates+=("traex")
3103
+
3104
+ for candidate in "${candidates[@]}"; do
3105
+ resolved="$(resolve_trae_cli_candidate "$candidate" || true)"
3106
+ [ -n "$resolved" ] || continue
3107
+ case "$emitted" in
3108
+ *"|$resolved|"*) continue ;;
3109
+ esac
3110
+ emitted="${emitted}|$resolved|"
3111
+ printf '%s\n' "$resolved"
3112
+ done
3113
+ }
3114
+
3115
+ find_coco_cli_candidates() {
3116
+ local candidate resolved emitted=""
3117
+ local candidates=()
3118
+ if [ -n "$TRAE_CLI_BIN" ] && [ "$(basename "$TRAE_CLI_BIN")" = "coco" ]; then candidates+=("$TRAE_CLI_BIN"); fi
3119
+ if [ -n "$AAMP_TRAE_CLI_BIN" ] && [ "$(basename "$AAMP_TRAE_CLI_BIN")" = "coco" ]; then candidates+=("$AAMP_TRAE_CLI_BIN"); fi
3120
+ candidates+=("coco")
3121
+ for candidate in "${candidates[@]}"; do
3122
+ resolved="$(resolve_trae_cli_candidate "$candidate" || true)"
3123
+ [ -n "$resolved" ] || continue
3124
+ case "$emitted" in *"|$resolved|"*) continue ;; esac
3125
+ emitted="${emitted}|$resolved|"
3126
+ printf '%s\n' "$resolved"
3127
+ done
3128
+ }
3129
+
3130
+ find_traecode_cli_candidates() {
3131
+ local candidate resolved emitted=""
3132
+ local candidates=()
3133
+ [ -n "$TRAECODE_CLI_BIN" ] && candidates+=("$TRAECODE_CLI_BIN")
3134
+ [ -n "$AAMP_TRAECODE_CLI_BIN" ] && candidates+=("$AAMP_TRAECODE_CLI_BIN")
3135
+ candidates+=("traecli")
3136
+ for candidate in "${candidates[@]}"; do
3137
+ resolved="$(resolve_trae_cli_candidate "$candidate" || true)"
3138
+ [ -n "$resolved" ] || continue
3139
+ case "$emitted" in *"|$resolved|"*) continue ;; esac
3140
+ emitted="${emitted}|$resolved|"
3141
+ printf '%s\n' "$resolved"
3142
+ done
3143
+ }
3144
+
3145
+ real_executable_path() {
3146
+ node -e 'const fs=require("fs"); try { process.stdout.write(fs.realpathSync(process.argv[1])); } catch { process.exit(1); }' "$1" 2>/dev/null
3147
+ }
3148
+
3149
+ find_traex_cli() {
3150
+ local candidate
3151
+ candidate="$(find_traex_cli_candidates | head -n 1)"
3152
+ if [ -n "$candidate" ]; then
3153
+ TRAE_CLI_BIN="$candidate"
3154
+ printf '%s\n' "$candidate"
3155
+ return 0
3156
+ fi
3157
+ return 1
3158
+ }
3159
+
3160
+ find_coco_cli() {
3161
+ local candidate
3162
+ candidate="$(find_coco_cli_candidates | head -n 1)"
3163
+ [ -n "$candidate" ] || return 1
3164
+ TRAE_CLI_BIN="$candidate"
3165
+ printf '%s\n' "$candidate"
3166
+ }
3167
+
3168
+ traecode_cli_conflicts_with_coco() {
3169
+ local traecli_bin="$1" coco_bin traecli_real coco_real
3170
+ coco_bin="$(find_coco_cli || true)"
3171
+ [ -n "$coco_bin" ] || return 1
3172
+ traecli_real="$(real_executable_path "$traecli_bin" || true)"
3173
+ coco_real="$(real_executable_path "$coco_bin" || true)"
3174
+ [ -n "$traecli_real" ] && [ "$traecli_real" = "$coco_real" ]
3175
+ }
3176
+
3177
+ find_traecode_cli() {
3178
+ local candidate explicit_candidate="" conflict_found="false"
3179
+ if [ -n "$TRAECODE_CLI_BIN" ]; then explicit_candidate="$TRAECODE_CLI_BIN"; fi
3180
+ if [ -n "$AAMP_TRAECODE_CLI_BIN" ]; then explicit_candidate="$AAMP_TRAECODE_CLI_BIN"; fi
3181
+ if [ -n "$explicit_candidate" ]; then
3182
+ case "$(basename "$explicit_candidate")" in coco|traex) return 2 ;; esac
3183
+ candidate="$(resolve_trae_cli_candidate "$explicit_candidate" || true)"
3184
+ [ -n "$candidate" ] || return 1
3185
+ if traecode_cli_conflicts_with_coco "$candidate"; then return 2; fi
3186
+ TRAECODE_CLI_BIN="$candidate"
3187
+ printf '%s\n' "$candidate"
3188
+ return 0
3189
+ fi
3190
+ while IFS= read -r candidate; do
3191
+ [ -n "$candidate" ] || continue
3192
+ if traecode_cli_conflicts_with_coco "$candidate"; then
3193
+ if [ -n "$explicit_candidate" ] && [ "$candidate" = "$explicit_candidate" ]; then return 2; fi
3194
+ conflict_found="true"
3195
+ continue
3196
+ fi
3197
+ TRAECODE_CLI_BIN="$candidate"
3198
+ printf '%s\n' "$candidate"
3199
+ return 0
3200
+ done < <(find_traecode_cli_candidates)
3201
+ [ "$conflict_found" != "true" ] || return 2
3202
+ return 1
3203
+ }
3204
+
3205
+ find_legacy_trae_cli() {
3206
+ find_coco_cli
3207
+ }
3208
+
3209
+ resolve_trae_cli() {
3210
+ case "${AGENT:-}" in
3211
+ traex) find_traex_cli ;;
3212
+ coco) find_coco_cli ;;
3213
+ traecli) find_traecode_cli ;;
3214
+ *) return 1 ;;
3215
+ esac
3216
+ }
3217
+
3218
+ find_workbuddy_cli() {
3219
+ is_macos || return 1
3220
+ [ -x "$WORKBUDDY_APP_CLI" ] || return 1
3221
+ printf '%s\n' "$WORKBUDDY_APP_CLI"
3222
+ }
3223
+
3224
+ find_workbuddy_ai_cli() {
3225
+ is_macos || return 1
3226
+ [ -x "$WORKBUDDY_AI_APP_CLI" ] || return 1
3227
+ printf '%s\n' "$WORKBUDDY_AI_APP_CLI"
3228
+ }
3229
+
3230
+ aime_acp_package_spec() {
3231
+ local default_spec registry spec
3232
+ spec="${AIME_ACP_PKG:-}"
3233
+ default_spec="$(aime_acp_default_package_spec)"
3234
+ registry="${AIME_ACP_REGISTRY:-https://bnpm.byted.org}"
3235
+ case "$spec" in
3236
+ @*/aime-acp@?*)
3237
+ if [ "${registry%/}" = 'https://bnpm.byted.org' ]; then
3238
+ printf '%s\n' "$spec"
3239
+ else
3240
+ printf '%s\n' "$default_spec"
3241
+ fi
3242
+ ;;
3243
+ *.tgz)
3244
+ if [ -f "$spec" ]; then
3245
+ printf '%s\n' "$spec"
3246
+ else
3247
+ printf '%s\n' "$default_spec"
3248
+ fi
3249
+ ;;
3250
+ *) printf '%s\n' "$default_spec" ;;
3251
+ esac
3252
+ }
3253
+
3254
+ aime_acp_default_package_spec() {
3255
+ printf '%s\n' "${AAMP_TASK_DEFAULT_AIME_ACP_PKG:-@tengchengwei/aime-acp@0.1.1-dev.1}"
3256
+ }
3257
+
3258
+ aime_acp_local_package_name() {
3259
+ local spec="$1"
3260
+ tar -xOzf "$spec" package/package.json 2>/dev/null | node -e '
3261
+ let raw = "";
3262
+ process.stdin.setEncoding("utf8");
3263
+ process.stdin.on("data", (chunk) => { raw += chunk; });
3264
+ process.stdin.on("end", () => {
3265
+ try {
3266
+ const value = JSON.parse(raw);
3267
+ if (typeof value.name !== "string" || !/^@[^/]+\/aime-acp$/.test(value.name)) process.exit(1);
3268
+ process.stdout.write(value.name);
3269
+ } catch {
3270
+ process.exit(1);
3271
+ }
3272
+ });
3273
+ '
3274
+ }
3275
+
3276
+ aime_acp_package_name() {
3277
+ local local_name spec
3278
+ spec="$(aime_acp_package_spec)"
3279
+ case "$spec" in
3280
+ *.tgz)
3281
+ local_name="$(aime_acp_local_package_name "$spec" || true)"
3282
+ if [ -n "$local_name" ]; then
3283
+ printf '%s\n' "$local_name"
3284
+ return 0
3285
+ fi
3286
+ spec="$(aime_acp_default_package_spec)"
3287
+ ;;
3288
+ esac
3289
+ case "$spec" in
3290
+ @*/aime-acp@?*) printf '%s\n' "${spec%@*}" ;;
3291
+ *) return 1 ;;
3292
+ esac
3293
+ }
3294
+
3295
+ aime_acp_package_version() {
3296
+ local spec
3297
+ spec="$(aime_acp_package_spec)"
3298
+ case "$spec" in
3299
+ *.tgz) return 1 ;;
3300
+ *@?*) printf '%s\n' "${spec##*@}" ;;
3301
+ *) return 1 ;;
3302
+ esac
3303
+ }
3304
+
3305
+ aime_acp_package_spec_is_supported() {
3306
+ case "$(aime_acp_package_spec)" in
3307
+ *.tgz) return 0 ;;
3308
+ @*/aime-acp@?*) return 0 ;;
3309
+ *) return 1 ;;
3310
+ esac
3311
+ }
3312
+
3313
+ aime_acp_registry() {
3314
+ printf '%s\n' 'https://bnpm.byted.org'
3315
+ }
3316
+
3317
+ aime_acp_site() {
3318
+ printf '%s\n' 'cn'
3319
+ }
3320
+
3321
+ aime_acp_cli_path() {
3322
+ printf '%s/dist/bin.js\n' "$(aime_acp_package_dir)"
3323
+ }
3324
+
3325
+ remove_legacy_aime_acp() {
3326
+ local legacy_dir legacy_bin canonical_dir resolved_bin
3327
+ legacy_dir="$NPM_GLOBAL_PREFIX/lib/node_modules/aime-acp"
3328
+ legacy_bin="$NPM_GLOBAL_PREFIX/bin/aime-acp"
3329
+ canonical_dir="$(aime_acp_package_dir)"
3330
+ canonical_dir="$(node -e '
3331
+ const fs = require("node:fs");
3332
+ try { process.stdout.write(fs.realpathSync(process.argv[1])); } catch {}
3333
+ ' "$canonical_dir")"
3334
+ if [ -e "$legacy_dir" ] || [ -L "$legacy_dir" ]; then
3335
+ rm -rf -- "$legacy_dir"
3336
+ fi
3337
+ if [ -e "$legacy_bin" ] || [ -L "$legacy_bin" ]; then
3338
+ resolved_bin="$(node -e '
3339
+ const fs = require("node:fs");
3340
+ const path = require("node:path");
3341
+ try { process.stdout.write(fs.realpathSync(process.argv[1])); } catch {}
3342
+ ' "$legacy_bin")"
3343
+ if [ -n "$canonical_dir" ] && [ -n "$resolved_bin" ]; then
3344
+ case "$resolved_bin" in
3345
+ "$canonical_dir"/*) return 0 ;;
3346
+ esac
3347
+ fi
3348
+ rm -f -- "$legacy_bin"
3349
+ fi
3350
+ }
3351
+
3352
+ aime_acp_package_dir() {
3353
+ printf '%s/lib/node_modules/%s\n' "$NPM_GLOBAL_PREFIX" "$(aime_acp_package_name)"
3354
+ }
3355
+
3356
+ aime_acp_installed_version() {
3357
+ local package_json
3358
+ package_json="$(aime_acp_package_dir)/package.json"
3359
+ [ -r "$package_json" ] || return 1
3360
+ node -e '
3361
+ const fs = require("node:fs");
3362
+ const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
3363
+ if (typeof value.version !== "string") process.exit(1);
3364
+ process.stdout.write(value.version);
3365
+ ' "$package_json"
3366
+ }
3367
+
3368
+ aime_acp_installed_name() {
3369
+ local package_json
3370
+ package_json="$(aime_acp_package_dir)/package.json"
3371
+ [ -r "$package_json" ] || return 1
3372
+ node -e '
3373
+ const fs = require("node:fs");
3374
+ const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
3375
+ if (typeof value.name !== "string") process.exit(1);
3376
+ process.stdout.write(value.name);
3377
+ ' "$package_json"
3378
+ }
3379
+
3380
+ aime_acp_install_is_valid() {
3381
+ local installed_version installed_name
3382
+ [ -x "$(aime_acp_cli_path)" ] || return 1
3383
+ installed_name="$(aime_acp_installed_name || true)"
3384
+ [ "$installed_name" = "$(aime_acp_package_name)" ] || return 1
3385
+ installed_version="$(aime_acp_installed_version || true)"
3386
+ [ -n "$installed_version" ] || return 1
3387
+ case "$(aime_acp_package_spec)" in
3388
+ *.tgz) return 0 ;;
3389
+ *) [ "$installed_version" = "$(aime_acp_package_version)" ] ;;
3390
+ esac
3391
+ }
3392
+
3393
+ aime_acp_install_is_current() {
3394
+ case "$(aime_acp_package_spec)" in
3395
+ *.tgz) return 1 ;;
3396
+ *) aime_acp_install_is_valid ;;
3397
+ esac
3398
+ }
3399
+
3400
+ ensure_aime_acp_cli() {
3401
+ aime_acp_package_spec_is_supported \
3402
+ || agent_fail "AIME_ACP_PACKAGE_INVALID: AIME ACP must use a scoped BNPM package or a local tgz."
3403
+ remove_legacy_aime_acp
3404
+ if aime_acp_install_is_current; then
3405
+ return 0
3406
+ fi
3407
+ agent_log "正在安装固定版本 AIME ACP 到隔离运行环境。"
3408
+ npm_install_global_from_registry "$(aime_acp_registry)" "$(aime_acp_package_spec)" \
3409
+ || agent_fail "AIME_ACP_INSTALL_FAILED: AIME ACP installation failed. Confirm company network access and retry."
3410
+ aime_acp_install_is_valid \
3411
+ || agent_fail "AIME_ACP_INSTALL_INVALID: AIME ACP installation validation failed."
3412
+ }
3413
+
2680
3414
  resolve_cursor_cli_for_acp() {
2681
3415
  command -v cursor
2682
3416
  }
@@ -2719,6 +3453,50 @@ ensure_agent_cli() {
2719
3453
  return 0
2720
3454
  fi
2721
3455
 
3456
+ if [ "$AGENT" = "traex" ]; then
3457
+ find_traex_cli >/dev/null 2>&1 || agent_fail "未检测到 traex CLI。请先安装 Trae CLI Next(内部版)后重新运行脚本。"
3458
+ return 0
3459
+ fi
3460
+
3461
+ if [ "$AGENT" = "traecli" ]; then
3462
+ local traecode_status
3463
+ set +e
3464
+ find_traecode_cli >/dev/null 2>&1
3465
+ traecode_status=$?
3466
+ set -e
3467
+ [ "$traecode_status" -ne 2 ] || agent_fail "检测到的 traecli 属于 Trae CLI(内部版),不能作为 TraeCode CLI 启动。"
3468
+ [ "$traecode_status" -eq 0 ] || agent_fail "未检测到 TraeCode CLI。请先安装 traecli 后重新运行脚本。"
3469
+ return 0
3470
+ fi
3471
+
3472
+ if [ "$AGENT" = "coco" ]; then
3473
+ if find_traex_cli >/dev/null 2>&1; then return 0; fi
3474
+ if find_coco_cli >/dev/null 2>&1; then return 0; fi
3475
+ if find_traecode_cli >/dev/null 2>&1; then return 0; fi
3476
+ agent_fail "未检测到 Trae CLI(内部版)、Trae CLI Next(内部版)或 TraeCode CLI。"
3477
+ fi
3478
+
3479
+ if [ "$AGENT" = "workbuddy" ]; then
3480
+ is_macos || agent_fail "WorkBuddy 一键探测仅支持 macOS。"
3481
+ find_workbuddy_cli >/dev/null 2>&1 \
3482
+ || agent_fail "未检测到 WorkBuddy。请确认已安装到 /Applications/WorkBuddy.app 后重新运行脚本。"
3483
+ return 0
3484
+ fi
3485
+
3486
+ if [ "$AGENT" = "workbuddy_ai" ]; then
3487
+ is_macos || agent_fail "WorkBuddy AI 一键探测仅支持 macOS。"
3488
+ find_workbuddy_ai_cli >/dev/null 2>&1 \
3489
+ || agent_fail "未检测到 WorkBuddy AI CLI:${WORKBUDDY_AI_APP_CLI}。请确认该文件存在且可执行后重新运行脚本。"
3490
+ return 0
3491
+ fi
3492
+
3493
+ if [ "$AGENT" = "aime" ]; then
3494
+ aime_internal_network_reachable \
3495
+ || agent_fail "AIME 仅在公司内网可用;当前无法 ping 通 aime.bytedance.net。"
3496
+ ensure_aime_acp_cli
3497
+ return 0
3498
+ fi
3499
+
2722
3500
  command -v "$AGENT" >/dev/null 2>&1 || agent_fail "未检测到 $AGENT CLI。请先安装后重新运行脚本。"
2723
3501
  }
2724
3502
 
@@ -2866,6 +3644,68 @@ resolve_latest_codex_cli_version() {
2866
3644
  --prefix "$NPM_GLOBAL_PREFIX" 2>>"$ONE_CLICK_LOG" | tail -n 1 | tr -d '[:space:]'
2867
3645
  }
2868
3646
 
3647
+ codex_update_cache_latest() {
3648
+ local current_version="$1"
3649
+ [ -n "${CODEX_UPDATE_CACHE_FILE:-}" ] || return 1
3650
+ [ -n "$current_version" ] || return 1
3651
+ [ -f "$CODEX_UPDATE_CACHE_FILE" ] || return 1
3652
+ CACHE_FILE="$CODEX_UPDATE_CACHE_FILE" \
3653
+ TTL="${CODEX_UPDATE_CACHE_TTL_SECONDS:-86400}" \
3654
+ CURRENT_VERSION="$current_version" \
3655
+ NPM_PACKAGE="${CODEX_NPM_PACKAGE:-@openai/codex}" \
3656
+ NPM_REGISTRY_VALUE="${NPM_REGISTRY:-https://registry.npmjs.org/}" \
3657
+ node -e '
3658
+ const fs = require("fs");
3659
+ let data;
3660
+ try { data = JSON.parse(fs.readFileSync(process.env.CACHE_FILE, "utf8")); } catch { process.exit(1); }
3661
+ const checkedAt = Number(data.checked_at || 0);
3662
+ const ttl = Number(process.env.TTL || 86400);
3663
+ const age = Math.floor(Date.now() / 1000) - checkedAt;
3664
+ if (data.version !== 1 || typeof data.checked_at !== "number" || !Number.isFinite(checkedAt) || checkedAt <= 0) process.exit(1);
3665
+ if (!Number.isFinite(ttl) || ttl <= 0 || age < 0 || age >= ttl) process.exit(1);
3666
+ if (typeof data.current_version !== "string" || typeof data.latest_version !== "string") process.exit(1);
3667
+ if (typeof data.package !== "string" || typeof data.registry !== "string") process.exit(1);
3668
+ if (String(data.current_version || "") !== String(process.env.CURRENT_VERSION || "")) process.exit(1);
3669
+ if (String(data.package || "") !== String(process.env.NPM_PACKAGE || "")) process.exit(1);
3670
+ if (String(data.registry || "") !== String(process.env.NPM_REGISTRY_VALUE || "")) process.exit(1);
3671
+ const latest = String(data.latest_version || "").trim();
3672
+ if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(latest)) process.exit(1);
3673
+ process.stdout.write(latest);
3674
+ ' 2>>"${ONE_CLICK_LOG:-/dev/null}"
3675
+ }
3676
+
3677
+ write_codex_update_cache() {
3678
+ local current_version="$1"
3679
+ local latest_version="$2"
3680
+ [ -n "${CODEX_UPDATE_CACHE_FILE:-}" ] || return 0
3681
+ [ -n "$current_version" ] && [ -n "$latest_version" ] || return 0
3682
+ mkdir -p "$(dirname "$CODEX_UPDATE_CACHE_FILE")"
3683
+ CACHE_FILE="$CODEX_UPDATE_CACHE_FILE" \
3684
+ CURRENT_VERSION="$current_version" \
3685
+ LATEST_VERSION="$latest_version" \
3686
+ NPM_PACKAGE="${CODEX_NPM_PACKAGE:-@openai/codex}" \
3687
+ NPM_REGISTRY_VALUE="${NPM_REGISTRY:-https://registry.npmjs.org/}" \
3688
+ node -e '
3689
+ const fs = require("fs");
3690
+ const file = process.env.CACHE_FILE;
3691
+ const temporary = `${file}.tmp.${process.pid}`;
3692
+ const payload = {
3693
+ version: 1,
3694
+ checked_at: Math.floor(Date.now() / 1000),
3695
+ current_version: process.env.CURRENT_VERSION || "",
3696
+ latest_version: process.env.LATEST_VERSION || "",
3697
+ package: process.env.NPM_PACKAGE || "",
3698
+ registry: process.env.NPM_REGISTRY_VALUE || "",
3699
+ };
3700
+ try {
3701
+ fs.writeFileSync(temporary, JSON.stringify(payload, null, 2) + "\n", { mode: 0o600 });
3702
+ fs.renameSync(temporary, file);
3703
+ } finally {
3704
+ try { fs.unlinkSync(temporary); } catch {}
3705
+ }
3706
+ ' 2>>"${ONE_CLICK_LOG:-/dev/null}" || true
3707
+ }
3708
+
2869
3709
  codex_cli_update_available() {
2870
3710
  CODEX_CURRENT_VERSION="$1" CODEX_LATEST_VERSION="$2" node -e '
2871
3711
  function parse(value) {
@@ -3004,7 +3844,13 @@ ensure_codex_cli_updated() {
3004
3844
  codex_bin="$(resolve_codex_cli_for_acp || true)"
3005
3845
  [ -n "$codex_bin" ] || return 0
3006
3846
  version_before="$(codex_cli_version_number "$codex_bin" || true)"
3007
- latest_version="$(resolve_latest_codex_cli_version || true)"
3847
+ latest_version="$(codex_update_cache_latest "$version_before" || true)"
3848
+ if [ -n "$latest_version" ]; then
3849
+ agent_detail "Codex CLI update check cache is fresh: current=${version_before:-unknown} latest=$latest_version"
3850
+ else
3851
+ latest_version="$(resolve_latest_codex_cli_version || true)"
3852
+ write_codex_update_cache "$version_before" "$latest_version"
3853
+ fi
3008
3854
  version_line="当前 Codex CLI 版本是:${version_before:-未知},最新版本是:${latest_version:-获取失败}"
3009
3855
  write_one_click_log "[aamp-one-click] $version_line"
3010
3856
  agent_detail "checking Codex CLI update: path=$codex_bin version=${version_before:-unknown}"
@@ -3015,7 +3861,6 @@ ensure_codex_cli_updated() {
3015
3861
  comparison_status=$?
3016
3862
  fi
3017
3863
  if [ "$comparison_status" -eq 1 ]; then
3018
- printf '%s\n' "$version_line"
3019
3864
  agent_detail "Codex CLI is already current: current=${version_before:-unknown} latest=${latest_version:-unknown}"
3020
3865
  return 0
3021
3866
  fi
@@ -3104,6 +3949,142 @@ run_codex_login() {
3104
3949
  return "$status"
3105
3950
  }
3106
3951
 
3952
+ run_quiet_command_with_timeout() {
3953
+ local timeout_seconds="$1"
3954
+ shift
3955
+ node - "$timeout_seconds" "$@" <<'NODE'
3956
+ const { spawn } = require('node:child_process');
3957
+
3958
+ const timeoutSeconds = Number(process.argv[2] || '10');
3959
+ const timeoutMs = Math.max(1, Number.isFinite(timeoutSeconds) ? timeoutSeconds : 10) * 1000;
3960
+ const [command, ...args] = process.argv.slice(3);
3961
+ if (!command) process.exit(127);
3962
+
3963
+ let finished = false;
3964
+ let timedOut = false;
3965
+ const child = spawn(command, args, { stdio: ['ignore', 'ignore', 'ignore'] });
3966
+ const timer = setTimeout(() => {
3967
+ timedOut = true;
3968
+ child.kill('SIGTERM');
3969
+ setTimeout(() => child.kill('SIGKILL'), 1000).unref();
3970
+ }, timeoutMs);
3971
+
3972
+ child.once('error', (error) => {
3973
+ if (finished) return;
3974
+ finished = true;
3975
+ clearTimeout(timer);
3976
+ process.exit(error && error.code === 'ENOENT' ? 127 : 1);
3977
+ });
3978
+
3979
+ child.once('exit', (code, signal) => {
3980
+ if (finished) return;
3981
+ finished = true;
3982
+ clearTimeout(timer);
3983
+ if (timedOut) process.exit(124);
3984
+ if (typeof code === 'number') process.exit(code);
3985
+ process.exit(signal ? 1 : 0);
3986
+ });
3987
+ NODE
3988
+ }
3989
+
3990
+ run_trae_login_status_for_bin() {
3991
+ local trae_bin="$1"
3992
+ run_quiet_command_with_timeout "$AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS" "$trae_bin" login status
3993
+ }
3994
+
3995
+ trae_login_status_timeout_message() {
3996
+ printf '%s' "traex login status 超时。请手动执行 'traex login status' 检查登录状态后重新运行脚本。"
3997
+ }
3998
+
3999
+ run_traex_login_status() {
4000
+ local trae_bin status
4001
+ trae_bin="$(find_traex_cli || true)"
4002
+ [ -n "$trae_bin" ] || return 127
4003
+ TRAE_CLI_BIN="$trae_bin"
4004
+ agent_detail "checking Trae CLI login status: $trae_bin"
4005
+ if run_trae_login_status_for_bin "$trae_bin"; then
4006
+ return 0
4007
+ else
4008
+ status=$?
4009
+ fi
4010
+ if [ "$status" -eq 124 ]; then
4011
+ agent_log "Trae CLI login status timed out after ${AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS}s: $trae_bin"
4012
+ fi
4013
+ return "$status"
4014
+ }
4015
+
4016
+ run_traex_login() {
4017
+ local trae_bin
4018
+ trae_bin="$(resolve_trae_cli || true)"
4019
+ [ -n "$trae_bin" ] || return 127
4020
+ "$trae_bin" login
4021
+ }
4022
+
4023
+ trae_cli_is_traex() {
4024
+ [ -n "$TRAE_CLI_BIN" ] && [ "${TRAE_CLI_BIN##*/}" = "traex" ]
4025
+ }
4026
+
4027
+ confirm_trae_upgrade() {
4028
+ local answer
4029
+ printf '\n检测到 Trae CLI(内部版),飞书任务 Agent 暂不支持使用该版本建立连接。\n' >&2
4030
+ printf '建议升级到 Trae CLI Next(内部版)(traex)后继续。\n' >&2
4031
+ printf '升级过程中可自行选择是否保留 Trae CLI(内部版)。\n' >&2
4032
+ answer="$(read_tty_line "是否现在升级到 traex?[y/N] ")"
4033
+ case "$answer" in
4034
+ y|Y|yes|YES|Yes) return 0 ;;
4035
+ *) return 1 ;;
4036
+ esac
4037
+ }
4038
+
4039
+ run_traex_installer() {
4040
+ agent_log "正在安装/升级 traex..."
4041
+ curl -fsSL "$TRAEX_INSTALLER_URL" | sh
4042
+ }
4043
+
4044
+ maybe_upgrade_legacy_trae_cli() {
4045
+ if trae_cli_is_traex; then
4046
+ AGENT="traex"
4047
+ return 0
4048
+ fi
4049
+
4050
+ if confirm_trae_upgrade; then
4051
+ run_traex_installer || agent_fail "traex 安装失败。请手动执行:curl -fsSL $TRAEX_INSTALLER_URL | sh"
4052
+ hash -r 2>/dev/null || true
4053
+ TRAE_CLI_BIN=""
4054
+ find_traex_cli >/dev/null 2>&1 || agent_fail "traex 安装后仍未检测到。请确认 traex 在 PATH 中,然后重新运行脚本。"
4055
+ AGENT="traex"
4056
+ agent_log "已切换为 traex,继续启动。"
4057
+ return 0
4058
+ fi
4059
+
4060
+ AGENT_PREPARE_CANCELLED="true"
4061
+ AGENT_PREPARE_CANCEL_REASON="用户取消升级。飞书任务 Agent 暂不支持使用 Trae CLI(内部版)建立连接,本次未启动飞书任务连接。如需继续,请先安装 Trae CLI Next(内部版):curl -fsSL $TRAEX_INSTALLER_URL | sh"
4062
+ agent_log "已取消升级,本次未启动飞书任务连接。"
4063
+ }
4064
+
4065
+ ensure_traex_login() {
4066
+ local trae_login_status
4067
+ set +e
4068
+ run_traex_login_status
4069
+ trae_login_status=$?
4070
+ set -e
4071
+ if [ "$trae_login_status" -eq 124 ]; then
4072
+ agent_fail "$(trae_login_status_timeout_message)"
4073
+ fi
4074
+ if [ "$trae_login_status" -ne 0 ]; then
4075
+ agent_log "Trae CLI Next(内部版)未登录,正在启动登录流程。"
4076
+ run_traex_login || agent_fail "Trae CLI Next(内部版)登录失败。请先执行 'traex login' 完成登录后重新运行脚本。"
4077
+ set +e
4078
+ run_traex_login_status
4079
+ trae_login_status=$?
4080
+ set -e
4081
+ if [ "$trae_login_status" -eq 124 ]; then
4082
+ agent_fail "$(trae_login_status_timeout_message)"
4083
+ fi
4084
+ [ "$trae_login_status" -eq 0 ] || agent_fail "Trae CLI Next(内部版)仍未登录。请先执行 'traex login' 完成登录后重新运行脚本。"
4085
+ fi
4086
+ }
4087
+
3107
4088
  print_cursor_gatekeeper_help() {
3108
4089
  local cursor_bin
3109
4090
  local cursor_real
@@ -3365,6 +4346,144 @@ ensure_codem_provider_ready() {
3365
4346
  return "$status"
3366
4347
  }
3367
4348
 
4349
+ traecode_readiness_helper_path() {
4350
+ if [ -n "$AAMP_TRAECODE_READINESS_HELPER" ]; then printf '%s\n' "$AAMP_TRAECODE_READINESS_HELPER"; return 0; fi
4351
+ local helper
4352
+ helper="$(task_agent_global_package_dir)/bin/traecode-readiness.mjs"
4353
+ [ -r "$helper" ] || return 1
4354
+ printf '%s\n' "$helper"
4355
+ }
4356
+
4357
+ run_traecode_check() {
4358
+ local action="$1" helper traecode_bin
4359
+ helper="$(traecode_readiness_helper_path)" || return 127
4360
+ traecode_bin="$(find_traecode_cli)" || return $?
4361
+ node "$helper" "$action" "$traecode_bin" "$AAMP_TRAECODE_CHECK_TIMEOUT_SECONDS"
4362
+ }
4363
+
4364
+ confirm_traecode_update() {
4365
+ local answer tty_path
4366
+ tty_path="${AAMP_TRAECODE_UPDATE_TTY:-/dev/tty}"
4367
+ if ! exec 6<>"$tty_path"; then return 2; fi
4368
+ printf '%s' '当前 TraeCode CLI 版本较旧,不支持 ACP。是否执行 `traecli update` 升级?[y/N] ' >&6
4369
+ if ! IFS= read -r answer <&6; then exec 6>&-; return 2; fi
4370
+ exec 6>&-
4371
+ case "$answer" in y|Y|yes|YES|Yes) return 0 ;; *) return 1 ;; esac
4372
+ }
4373
+
4374
+ run_traecode_update() {
4375
+ local traecode_bin="$1"
4376
+ [ -n "$traecode_bin" ] || return 127
4377
+ "$traecode_bin" update
4378
+ }
4379
+
4380
+ ensure_traecode_acp() {
4381
+ local probe_status confirm_status update_bin
4382
+ set +e; run_traecode_check probe-acp >/dev/null; probe_status=$?; set -e
4383
+ [ "$probe_status" -eq 0 ] && return 0
4384
+ [ "$probe_status" -ne 124 ] || agent_fail "TraeCode CLI ACP 能力检查超时,请稍后重试。"
4385
+ [ "$probe_status" -eq 3 ] || agent_fail "无法检查 TraeCode CLI 的 ACP 能力。"
4386
+
4387
+ set +e; confirm_traecode_update; confirm_status=$?; set -e
4388
+ if [ "$confirm_status" -eq 2 ]; then
4389
+ agent_fail "当前 TraeCode CLI 需要升级。请在交互式终端执行 'traecli update' 后重试。"
4390
+ fi
4391
+ if [ "$confirm_status" -ne 0 ]; then
4392
+ AGENT_PREPARE_CANCELLED="true"
4393
+ AGENT_PREPARE_CANCEL_REASON="用户取消升级 TraeCode CLI,本次未启动飞书任务连接。"
4394
+ return 0
4395
+ fi
4396
+
4397
+ update_bin="$(find_traecode_cli)" || agent_fail "升级前无法重新定位 TraeCode CLI,请手动执行 'traecli update' 后重试。"
4398
+ run_traecode_update "$update_bin" || agent_fail "TraeCode CLI 升级失败,请查看上方升级输出,或手动执行 'traecli update' 后重试。"
4399
+ hash -r 2>/dev/null || true
4400
+ TRAECODE_CLI_BIN=""
4401
+ find_traecode_cli >/dev/null 2>&1 || agent_fail "升级后仍未检测到 TraeCode CLI,请检查 PATH 后重试。"
4402
+ set +e; run_traecode_check probe-acp >/dev/null; probe_status=$?; set -e
4403
+ [ "$probe_status" -eq 0 ] || agent_fail "TraeCode CLI 升级后仍不支持 ACP,请确认升级结果后重试。"
4404
+ }
4405
+
4406
+ ensure_traecode_doctor() {
4407
+ local output doctor_status
4408
+ set +e; output="$(run_traecode_check doctor 2>&1)"; doctor_status=$?; set -e
4409
+ case "$doctor_status" in
4410
+ 0) [ -z "$output" ] || printf 'TraeCode CLI 诊断警告:%s\n' "$output" ;;
4411
+ 10) agent_fail "TraeCode CLI 尚未准备好。请打开 TraeCode CLI,按提示完成登录,并使用 /model 选择模型后重试。" ;;
4412
+ 11) agent_fail "TraeCode CLI 诊断未通过:${output:-未知错误}" ;;
4413
+ 65) agent_fail "TraeCode CLI 诊断结果无法解析,请执行 'traecli doctor --json' 检查后重试。" ;;
4414
+ 124) agent_fail "TraeCode CLI 诊断超时,请稍后重试。" ;;
4415
+ *) agent_fail "TraeCode CLI 诊断执行失败:${output:-请执行 'traecli doctor --json' 检查。}" ;;
4416
+ esac
4417
+ }
4418
+
4419
+ ensure_traecode_ready() {
4420
+ ensure_traecode_acp
4421
+ [ "$AGENT_PREPARE_CANCELLED" = "true" ] && return 0
4422
+ ensure_traecode_doctor
4423
+ }
4424
+
4425
+ run_aime_auth_status() {
4426
+ "$(aime_acp_cli_path)" auth status --site "$(aime_acp_site)" --json
4427
+ }
4428
+
4429
+ run_aime_auth_login() {
4430
+ "$(aime_acp_cli_path)" auth login --site "$(aime_acp_site)"
4431
+ }
4432
+
4433
+ run_aime_doctor() {
4434
+ "$(aime_acp_cli_path)" doctor --site "$(aime_acp_site)" --json
4435
+ }
4436
+
4437
+ ensure_aime_ready() {
4438
+ local auth_output auth_status auth_kind login_status doctor_status
4439
+ set +e
4440
+ auth_output="$(run_aime_auth_status)"
4441
+ auth_status=$?
4442
+ set -e
4443
+ auth_kind="$(printf '%s' "$auth_output" | node -e '
4444
+ let input = "";
4445
+ process.stdin.setEncoding("utf8");
4446
+ process.stdin.on("data", (chunk) => { input += chunk; });
4447
+ process.stdin.on("end", () => {
4448
+ try {
4449
+ const value = JSON.parse(input.trim());
4450
+ if (value?.ok === true && value?.status === "authenticated") process.stdout.write("authenticated");
4451
+ else if (value?.ok === true && value?.status === "unauthenticated") process.stdout.write("unauthenticated");
4452
+ else process.stdout.write("error");
4453
+ } catch {
4454
+ process.stdout.write("error");
4455
+ }
4456
+ });
4457
+ ')"
4458
+ if [ "$auth_status" -eq 0 ] && [ "$auth_kind" = "authenticated" ]; then
4459
+ agent_detail "AIME managed-user authentication is ready"
4460
+ elif [ "$auth_status" -eq 1 ] && [ "$auth_kind" = "unauthenticated" ]; then
4461
+ agent_log "AIME 尚未登录,正在启动独立登录流程。"
4462
+ set +e
4463
+ run_aime_auth_login
4464
+ login_status=$?
4465
+ set -e
4466
+ case "$login_status" in
4467
+ 0) ;;
4468
+ 2)
4469
+ agent_fail "AIME 登录仍在等待完成。请继续完成浏览器授权,或重新执行 'aime-acp auth login --site cn' 后重试。"
4470
+ ;;
4471
+ *)
4472
+ agent_fail "AIME 登录失败。请执行 'aime-acp auth login --site cn' 后重试。"
4473
+ ;;
4474
+ esac
4475
+ else
4476
+ agent_fail "AIME 认证状态检查失败。请执行 'aime-acp auth status --site cn --json' 查看安全诊断。"
4477
+ fi
4478
+
4479
+ set +e
4480
+ run_aime_doctor >/dev/null
4481
+ doctor_status=$?
4482
+ set -e
4483
+ [ "$doctor_status" -eq 0 ] \
4484
+ || agent_fail "AIME doctor 未通过。请确认公司内网与账号状态,并执行 'aime-acp doctor --site cn --json' 查看安全诊断。"
4485
+ }
4486
+
3368
4487
  ensure_agent_login() {
3369
4488
  case "$AGENT" in
3370
4489
  codex)
@@ -3385,6 +4504,35 @@ ensure_agent_login() {
3385
4504
  run_cursor_login_status || agent_fail "cursor CLI 仍未登录。请先执行 'cursor login' 或 'agent login' 完成登录后重新运行脚本。"
3386
4505
  fi
3387
4506
  ;;
4507
+ traex)
4508
+ ensure_traex_login
4509
+ ;;
4510
+ traecli)
4511
+ ensure_traecode_ready
4512
+ ;;
4513
+ coco)
4514
+ if find_traex_cli >/dev/null 2>&1; then
4515
+ AGENT="traex"
4516
+ ensure_traex_login
4517
+ elif find_coco_cli >/dev/null 2>&1; then
4518
+ maybe_upgrade_legacy_trae_cli
4519
+ if [ "$AGENT" = "traex" ]; then ensure_traex_login; fi
4520
+ elif find_traecode_cli >/dev/null 2>&1; then
4521
+ AGENT="traecli"
4522
+ ensure_traecode_ready
4523
+ else
4524
+ agent_fail "未检测到 Trae CLI(内部版)、Trae CLI Next(内部版)或 TraeCode CLI。"
4525
+ fi
4526
+ ;;
4527
+ workbuddy)
4528
+ agent_detail "WorkBuddy authentication is managed by the desktop app"
4529
+ ;;
4530
+ workbuddy_ai)
4531
+ agent_detail "WorkBuddy AI authentication is managed by the desktop app"
4532
+ ;;
4533
+ aime)
4534
+ ensure_aime_ready
4535
+ ;;
3388
4536
  codem)
3389
4537
  ensure_codem_local_bin_on_path
3390
4538
  codem --version >/dev/null
@@ -3453,6 +4601,11 @@ resolve_codex_cli_for_acp() {
3453
4601
  return 0
3454
4602
  fi
3455
4603
 
4604
+ if is_macos && [ -x "$CODEX_CHATGPT_APP_CLI" ]; then
4605
+ printf '%s\n' "$CODEX_CHATGPT_APP_CLI"
4606
+ return 0
4607
+ fi
4608
+
3456
4609
  if is_macos && [ -x "$CODEX_APP_CLI" ]; then
3457
4610
  printf '%s\n' "$CODEX_APP_CLI"
3458
4611
  return 0
@@ -3461,8 +4614,69 @@ resolve_codex_cli_for_acp() {
3461
4614
  return 1
3462
4615
  }
3463
4616
 
4617
+ acp_command_word() {
4618
+ local value="$1" single_quote_escape
4619
+ case "$value" in *$'\n'*|*$'\r'*) return 1 ;; esac
4620
+ single_quote_escape="'\"'\"'"
4621
+ value="${value//\'/$single_quote_escape}"
4622
+ printf "'%s'" "$value"
4623
+ }
4624
+
3464
4625
  build_acp_agent_command() {
3465
4626
  ACP_AGENT_COMMAND="$AGENT"
4627
+ if [ "$AGENT" = "workbuddy" ]; then
4628
+ local workbuddy_bin workbuddy_config_dir_word
4629
+ workbuddy_bin="$(find_workbuddy_cli)" \
4630
+ || agent_fail "WorkBuddy 不可用。请确认已安装到 /Applications/WorkBuddy.app。"
4631
+ workbuddy_config_dir_word="$(acp_command_word "$HOME/.workbuddy")" \
4632
+ || agent_fail "WorkBuddy 配置目录包含不受支持的换行符。"
4633
+ ACP_AGENT_COMMAND="env CODEBUDDY_CONFIG_DIR=$workbuddy_config_dir_word CODEBUDDY_SKIP_BUILTIN_MARKETPLACE=1 $workbuddy_bin --acp"
4634
+ agent_detail "using native WorkBuddy ACP command: $ACP_AGENT_COMMAND"
4635
+ return 0
4636
+ fi
4637
+
4638
+ if [ "$AGENT" = "workbuddy_ai" ]; then
4639
+ local workbuddy_ai_bin workbuddy_ai_word workbuddy_ai_config_dir_word
4640
+ workbuddy_ai_bin="$(find_workbuddy_ai_cli)" \
4641
+ || agent_fail "WorkBuddy AI CLI 不可用:${WORKBUDDY_AI_APP_CLI}。请确认该文件存在且可执行。"
4642
+ workbuddy_ai_word="$(acp_command_word "$workbuddy_ai_bin")" \
4643
+ || agent_fail "WorkBuddy AI 路径包含不受支持的换行符。"
4644
+ workbuddy_ai_config_dir_word="$(acp_command_word "$HOME/.workbuddy-ai")" \
4645
+ || agent_fail "WorkBuddy AI 配置目录包含不受支持的换行符。"
4646
+ ACP_AGENT_COMMAND="env CODEBUDDY_CONFIG_DIR=$workbuddy_ai_config_dir_word CODEBUDDY_SKIP_BUILTIN_MARKETPLACE=1 $workbuddy_ai_word --acp"
4647
+ agent_detail "using native WorkBuddy AI ACP command: $ACP_AGENT_COMMAND"
4648
+ return 0
4649
+ fi
4650
+
4651
+ if [ "$AGENT" = "aime" ]; then
4652
+ local aime_bin aime_word
4653
+ aime_bin="$(aime_acp_cli_path)"
4654
+ [ -x "$aime_bin" ] || agent_fail "AIME_ACP_UNAVAILABLE: AIME ACP executable is unavailable after preparation."
4655
+ aime_word="$(acp_command_word "$aime_bin")" \
4656
+ || agent_fail "AIME_ACP_PATH_INVALID: AIME ACP executable path is invalid."
4657
+ ACP_AGENT_COMMAND="$aime_word --site $(aime_acp_site)"
4658
+ agent_detail "AIME ACP configured for site $(aime_acp_site)"
4659
+ return 0
4660
+ fi
4661
+
4662
+ if [ "$AGENT" = "traecli" ]; then
4663
+ local traecode_bin traecode_word
4664
+ traecode_bin="$(find_traecode_cli)" || agent_fail "TraeCode CLI 不可用,无法启动 ACP 服务。"
4665
+ traecode_word="$(acp_command_word "$traecode_bin")" \
4666
+ || agent_fail "TraeCode CLI 路径包含不受支持的换行符。"
4667
+ ACP_AGENT_COMMAND="$traecode_word acp serve"
4668
+ agent_detail "using native TraeCode ACP command: $ACP_AGENT_COMMAND"
4669
+ return 0
4670
+ fi
4671
+
4672
+ if [ "$AGENT" = "coco" ] || [ "$AGENT" = "traex" ]; then
4673
+ local trae_bin
4674
+ trae_bin="$(find_traex_cli)" || agent_fail "Trae CLI Next(内部版)(traex)不可用,无法启动飞书任务所需的 ACP 服务。"
4675
+ ACP_AGENT_COMMAND="$trae_bin acp serve"
4676
+ agent_detail "using native Trae ACP command: $ACP_AGENT_COMMAND"
4677
+ return 0
4678
+ fi
4679
+
3466
4680
  if [ "$AGENT" != "codex" ]; then
3467
4681
  return 0
3468
4682
  fi
@@ -3484,7 +4698,7 @@ validate_codex_acp_command() {
3484
4698
  if [ "$status" -ne 0 ]; then
3485
4699
  agent_log "Codex ACP command validation failed:"
3486
4700
  printf '%s\n' "$output" >&2
3487
- agent_fail "codex ACP command is unavailable; check Codex.app or reinstall codex CLI"
4701
+ agent_fail "codex ACP command is unavailable; check ChatGPT.app, Codex.app, or reinstall codex CLI"
3488
4702
  fi
3489
4703
  }
3490
4704
 
@@ -3683,6 +4897,22 @@ run_internal_discover_agents() {
3683
4897
  if agent_cli_detected cursor; then
3684
4898
  agents+=("cursor")
3685
4899
  fi
4900
+ if agent_cli_detected traex; then
4901
+ agents+=("traex")
4902
+ elif agent_cli_detected coco; then
4903
+ agents+=("coco")
4904
+ elif agent_cli_detected traecli; then
4905
+ agents+=("traecli")
4906
+ fi
4907
+ if agent_cli_detected workbuddy; then
4908
+ agents+=("workbuddy")
4909
+ fi
4910
+ if agent_cli_detected workbuddy_ai; then
4911
+ agents+=("workbuddy_ai")
4912
+ fi
4913
+ if agent_cli_detected aime; then
4914
+ agents+=("aime")
4915
+ fi
3686
4916
  local joined=""
3687
4917
  if [ "${#agents[@]}" -gt 0 ]; then
3688
4918
  joined="$(IFS=,; printf '%s' "${agents[*]}")"
@@ -3696,32 +4926,79 @@ process.stdout.write(JSON.stringify({ agents }));
3696
4926
  prepare_internal_agent_environment() {
3697
4927
  [ -n "$AGENT" ] || agent_fail "internal Agent preparation requires --agent"
3698
4928
  validate_agent_name "$AGENT"
3699
- source_lark_env
4929
+ ensure_agent_selection_available "$AGENT"
4930
+ load_agent_metadata
4931
+ if [ "$AGENT_EXECUTION_LOCATION" = "local" ]; then
4932
+ source_lark_env
4933
+ fi
3700
4934
  ensure_agent_cli
3701
4935
  }
3702
4936
 
3703
4937
  run_internal_register_binding() {
3704
4938
  [ -n "$AGENT" ] || agent_fail "internal Bot registration requires --agent"
3705
4939
  validate_agent_name "$AGENT"
3706
- source_lark_env
4940
+ ensure_agent_selection_available "$AGENT"
4941
+ load_agent_metadata
3707
4942
  register_feishu_app
3708
- emit_internal_result "{\"app_id\":\"$(json_escape "$APP_ID")\",\"app_secret\":\"$(json_escape "$APP_SECRET")\",\"display_name\":\"$(json_escape "$BOT_NAME")\",\"lark_cli_profile\":\"$(json_escape "$LARK_CLI_PROFILE")\"}"
4943
+ if [ "$AGENT_EXECUTION_LOCATION" = "remote" ]; then
4944
+ emit_internal_result "{\"app_id\":\"$(json_escape "$APP_ID")\",\"app_secret\":\"$(json_escape "$APP_SECRET")\",\"display_name\":\"$(json_escape "$BOT_NAME")\",\"auth_mode\":\"app-secret\"}"
4945
+ else
4946
+ emit_internal_result "{\"app_id\":\"$(json_escape "$APP_ID")\",\"app_secret\":\"$(json_escape "$APP_SECRET")\",\"display_name\":\"$(json_escape "$BOT_NAME")\",\"lark_cli_profile\":\"$(json_escape "$LARK_CLI_PROFILE")\",\"auth_mode\":\"lark-cli\"}"
4947
+ fi
3709
4948
  }
3710
4949
 
3711
4950
  run_internal_prepare_agent() {
3712
4951
  prepare_internal_agent_environment
3713
4952
  ensure_codex_cli_updated
3714
4953
  ensure_agent_login
4954
+ if [ "$AGENT_PREPARE_CANCELLED" = "true" ]; then
4955
+ emit_internal_result "{\"cancelled\":true,\"agent_type\":\"$(json_escape "$AGENT")\",\"reason\":\"$(json_escape "$AGENT_PREPARE_CANCEL_REASON")\"}"
4956
+ return 0
4957
+ fi
3715
4958
  maybe_mock_fail "agent-login"
3716
4959
  ensure_acpx
3717
4960
  build_acp_agent_command
3718
- emit_internal_result "{\"agent_type\":\"$(json_escape "$AGENT")\",\"acp_command\":\"$(json_escape "$ACP_AGENT_COMMAND")\",\"lark_cli_config_dir\":\"$(json_escape "${LARKSUITE_CLI_CONFIG_DIR:-}")\"}"
4961
+ if [ "$AGENT_EXECUTION_LOCATION" = "remote" ]; then
4962
+ emit_internal_result "{\"agent_type\":\"$(json_escape "$AGENT")\",\"acp_command\":\"$(json_escape "$ACP_AGENT_COMMAND")\"}"
4963
+ else
4964
+ emit_internal_result "{\"agent_type\":\"$(json_escape "$AGENT")\",\"acp_command\":\"$(json_escape "$ACP_AGENT_COMMAND")\",\"lark_cli_config_dir\":\"$(json_escape "${LARKSUITE_CLI_CONFIG_DIR:-}")\"}"
4965
+ fi
4966
+ }
4967
+
4968
+ run_internal_probe_profile() {
4969
+ AAMP_TASK_INTERNAL_BINDING_JSON=""
4970
+ IFS= read -r AAMP_TASK_INTERNAL_BINDING_JSON <&"$AAMP_TASK_INTERNAL_INPUT_FD" || true
4971
+ [ -n "$AAMP_TASK_INTERNAL_BINDING_JSON" ] || agent_fail "missing internal binding payload"
4972
+ AGENT="$(binding_json_field agent_type)"
4973
+ validate_agent_name "$AGENT"
4974
+ load_agent_metadata
4975
+ [ "$AGENT_EXECUTION_LOCATION" != "remote" ] || agent_fail "remote bindings do not use lark-cli profiles"
4976
+ APP_ID="$(binding_json_field bot.app_id)"
4977
+ LARK_CLI_PROFILE="$(binding_json_field bot.lark_cli_profile)"
4978
+ [ -n "$APP_ID" ] && [ -n "$LARK_CLI_PROFILE" ] || agent_fail "binding is missing Feishu app or profile"
4979
+ AAMP_TASK_INTERNAL_BINDING_JSON=""
4980
+ unset HTTPS_PROXY https_proxy HTTP_PROXY http_proxy ALL_PROXY all_proxy
4981
+ export LARKSUITE_CLI_CONFIG_DIR="$AAMP_LARK_CLI_CONFIG_DIR"
4982
+ unset LARK_CLI_NO_PROXY
4983
+ if ! select_lark_cli_bin_from_candidates; then
4984
+ emit_internal_result '{"ready":false}'
4985
+ return 0
4986
+ fi
4987
+ if with_lark_cli_config_lock probe_lark_cli_profile_locked "$LARK_CLI_PROFILE"; then
4988
+ emit_internal_result "{\"ready\":true,\"lark_cli_bin\":\"$(json_escape "$LARK_CLI_CMD")\",\"lark_cli_config_dir\":\"$(json_escape "${LARKSUITE_CLI_CONFIG_DIR:-}")\"}"
4989
+ else
4990
+ emit_internal_result '{"ready":false}'
4991
+ fi
3719
4992
  }
3720
4993
 
3721
4994
  run_internal_ensure_profile() {
3722
4995
  AAMP_TASK_INTERNAL_BINDING_JSON=""
3723
4996
  IFS= read -r AAMP_TASK_INTERNAL_BINDING_JSON <&"$AAMP_TASK_INTERNAL_INPUT_FD" || true
3724
4997
  [ -n "$AAMP_TASK_INTERNAL_BINDING_JSON" ] || agent_fail "missing internal binding payload"
4998
+ AGENT="$(binding_json_field agent_type)"
4999
+ validate_agent_name "$AGENT"
5000
+ load_agent_metadata
5001
+ [ "$AGENT_EXECUTION_LOCATION" != "remote" ] || agent_fail "remote bindings do not use lark-cli profiles"
3725
5002
  APP_ID="$(binding_json_field bot.app_id)"
3726
5003
  APP_SECRET="$(binding_json_field bot.app_secret)"
3727
5004
  BOT_NAME="$(binding_json_field bot.display_name)"
@@ -3736,7 +5013,9 @@ run_internal_ensure_profile() {
3736
5013
 
3737
5014
  run_internal_action() {
3738
5015
  AAMP_TASK_INTERNAL="true"
3739
- ensure_node_toolchain
5016
+ if [ "$AAMP_TASK_ACTION" != "__probe-profile" ]; then
5017
+ ensure_node_toolchain
5018
+ fi
3740
5019
  case "$AAMP_TASK_ACTION" in
3741
5020
  __discover-agents)
3742
5021
  run_internal_discover_agents
@@ -3747,6 +5026,9 @@ run_internal_action() {
3747
5026
  __prepare-agent)
3748
5027
  run_internal_prepare_agent
3749
5028
  ;;
5029
+ __probe-profile)
5030
+ run_internal_probe_profile
5031
+ ;;
3750
5032
  __ensure-profile)
3751
5033
  run_internal_ensure_profile
3752
5034
  ;;
@@ -3760,6 +5042,92 @@ task_agent_controller_path() {
3760
5042
  printf '%s/bin/feishu-task-agent-controller.mjs\n' "$(task_agent_global_package_dir)"
3761
5043
  }
3762
5044
 
5045
+ task_agent_metadata_path() {
5046
+ printf '%s/bin/agent-metadata.mjs\n' "$(task_agent_global_package_dir)"
5047
+ }
5048
+
5049
+ local_package_override_is_supported() {
5050
+ local spec local_path
5051
+ spec="${1:-}"
5052
+ case "$spec" in
5053
+ file:*)
5054
+ local_path="${spec#file:}"
5055
+ [ -n "$local_path" ] && [ -d "$local_path" ]
5056
+ ;;
5057
+ *.tgz) [ -f "$spec" ] ;;
5058
+ *) return 1 ;;
5059
+ esac
5060
+ }
5061
+
5062
+ aime_local_package_override_path() {
5063
+ local directory filename spec
5064
+ spec="${1:-}"
5065
+ case "$spec" in
5066
+ -*|file:*|*://*) return 1 ;;
5067
+ esac
5068
+ if [[ "$spec" =~ (^|@)[[:alpha:]][[:alnum:]+.-]*: ]]; then
5069
+ return 1
5070
+ fi
5071
+ case "$spec" in
5072
+ *.tgz) [ -f "$spec" ] || return 1 ;;
5073
+ *) return 1 ;;
5074
+ esac
5075
+ case "$spec" in
5076
+ */*)
5077
+ directory="${spec%/*}"
5078
+ filename="${spec##*/}"
5079
+ [ -n "$directory" ] || directory="/"
5080
+ ;;
5081
+ *)
5082
+ directory="."
5083
+ filename="$spec"
5084
+ ;;
5085
+ esac
5086
+ directory="$(cd -P -- "$directory" 2>/dev/null && pwd -P)" || return 1
5087
+ printf '%s/%s\n' "$directory" "$filename"
5088
+ }
5089
+
5090
+ apply_task_agent_package_override_policy() {
5091
+ local aime_override_path
5092
+ ACP_BRIDGE_PKG="$AAMP_TASK_DEFAULT_ACP_BRIDGE_PKG"
5093
+ FEISHU_BRIDGE_PKG="$AAMP_TASK_DEFAULT_FEISHU_BRIDGE_PKG"
5094
+ AIME_ACP_PKG="$AAMP_TASK_DEFAULT_AIME_ACP_PKG"
5095
+ [ "$AAMP_TASK_ALLOW_PACKAGE_OVERRIDES" = "true" ] || return 0
5096
+
5097
+ if [ -n "$AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG" ]; then
5098
+ local_package_override_is_supported "$AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG" \
5099
+ || agent_fail "Local ACP Bridge package override is invalid."
5100
+ ACP_BRIDGE_PKG="$AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG"
5101
+ fi
5102
+ if [ -n "$AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG" ]; then
5103
+ local_package_override_is_supported "$AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG" \
5104
+ || agent_fail "Local Feishu Bridge package override is invalid."
5105
+ FEISHU_BRIDGE_PKG="$AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG"
5106
+ fi
5107
+ if [ -n "$AAMP_TASK_REQUESTED_AIME_ACP_PKG" ]; then
5108
+ aime_override_path="$(aime_local_package_override_path "$AAMP_TASK_REQUESTED_AIME_ACP_PKG")" \
5109
+ || agent_fail "AIME_ACP_PACKAGE_OVERRIDE_INVALID: AIME ACP package override must be an existing local .tgz file."
5110
+ AAMP_TASK_REQUESTED_AIME_ACP_PKG="$aime_override_path"
5111
+ AIME_ACP_PKG="$aime_override_path"
5112
+ fi
5113
+ }
5114
+
5115
+ load_agent_metadata() {
5116
+ local metadata_path metadata_json
5117
+ metadata_path="$(task_agent_metadata_path)"
5118
+ [ -r "$metadata_path" ] || agent_fail "Task Agent metadata is missing: $metadata_path"
5119
+ metadata_json="$(node "$metadata_path" --json "$AGENT")" \
5120
+ || agent_fail "failed to resolve Task Agent metadata for $AGENT"
5121
+ AGENT_EXECUTION_LOCATION="$(node -e 'const value=JSON.parse(process.argv[1]);process.stdout.write(value.executionLocation)' "$metadata_json")"
5122
+ AGENT_ATTACHMENT_POLICY="$(node -e 'const value=JSON.parse(process.argv[1]);process.stdout.write(value.attachmentPolicy || "")' "$metadata_json")"
5123
+ AGENT_TASK_DISPATCH_CONCURRENCY="$(node -e 'const value=JSON.parse(process.argv[1]);process.stdout.write(value.taskDispatchConcurrency ? String(value.taskDispatchConcurrency) : "")' "$metadata_json")"
5124
+ if [ -n "$AAMP_TASK_INTERNAL_EXECUTION_LOCATION" ] \
5125
+ && [ "$AAMP_TASK_INTERNAL_EXECUTION_LOCATION" != "$AGENT_EXECUTION_LOCATION" ]; then
5126
+ agent_fail "Task Agent execution metadata mismatch"
5127
+ fi
5128
+ AAMP_TASK_INTERNAL_EXECUTION_LOCATION="$AGENT_EXECUTION_LOCATION"
5129
+ }
5130
+
3763
5131
  run_task_agent_controller() {
3764
5132
  local controller bootstrap_path install_command
3765
5133
  controller="$(task_agent_controller_path)"
@@ -3772,6 +5140,17 @@ run_task_agent_controller() {
3772
5140
  export AAMP_TASK_NPX_BIN="$NPX_BIN"
3773
5141
  export AAMP_TASK_ACP_BRIDGE_PKG="$ACP_BRIDGE_PKG"
3774
5142
  export AAMP_TASK_FEISHU_BRIDGE_PKG="$FEISHU_BRIDGE_PKG"
5143
+ export AAMP_TASK_AIME_ACP_PKG="$AIME_ACP_PKG"
5144
+ export AAMP_TASK_ALLOW_PACKAGE_OVERRIDES
5145
+ if [ "$AAMP_TASK_ALLOW_PACKAGE_OVERRIDES" != "true" ]; then
5146
+ AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG=""
5147
+ AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG=""
5148
+ AAMP_TASK_REQUESTED_AIME_ACP_PKG=""
5149
+ fi
5150
+ export AAMP_TASK_PACKAGE_OVERRIDES_RESOLVED=true
5151
+ export AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG
5152
+ export AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG
5153
+ export AAMP_TASK_REQUESTED_AIME_ACP_PKG
3775
5154
  export AAMP_TASK_CODEX_ACP_PKG="$CODEX_ACP_PKG"
3776
5155
  export AAMP_TASK_AGENT_VERSION
3777
5156
  export AAMP_TASK_DEFAULT_AGENT="$AGENT"
@@ -3829,13 +5208,19 @@ main() {
3829
5208
  return 0
3830
5209
  fi
3831
5210
 
5211
+ apply_task_agent_package_override_policy
5212
+
3832
5213
  case "$AAMP_TASK_ACTION" in
3833
- __discover-agents|__register-binding|__prepare-agent|__ensure-profile)
5214
+ __discover-agents|__register-binding|__prepare-agent|__probe-profile|__ensure-profile)
3834
5215
  run_internal_action
3835
5216
  return 0
3836
5217
  ;;
3837
5218
  esac
3838
5219
 
5220
+ if [ "$AGENT" = "aime" ]; then
5221
+ ensure_agent_selection_available "$AGENT"
5222
+ fi
5223
+
3839
5224
  trap cleanup EXIT INT TERM HUP
3840
5225
  init_log_run
3841
5226
  validate_mock_fail_stage