1PORTNAME=	claude-code
2DISTVERSION=	2.1.112
3CATEGORIES=	misc # machine-learning
4DISTFILES=	${PORTNAME}-${DISTVERSION}${EXTRACT_SUFX}
5
6MAINTAINER=	ports@MidnightBSD.org
7COMMENT=	Agentic coding tool from Anthropic that lives in your terminal
8WWW=		https://github.com/anthropics/claude-code
9
10LICENSE=	anthropic
11LICENSE_NAME=	Anthropic
12LICENSE_PERMS=	dist-mirror pkg-mirror
13LICENSE_TEXT=	See https://www.anthropic.com/legal/commercial-terms
14
15FETCH_DEPENDS=	cargo:lang/rust \
16		npm:www/npm \
17		jq:textproc/jq \
18		${LOCALBASE}/share/certs/ca-root-nss.crt:security/ca_root_nss
19EXTRACT_DEPENDS=	curl:ftp/curl
20RUN_DEPENDS=	rg:textproc/ripgrep
21
22USES=		nodejs:run
23
24JS_ARCH=	${ARCH:S/amd64/x64/:S/aarch64/arm64/:S/i386/ia32/:S/powerpc64le/ppc64le/:S/powerpc64/ppc64/:C/armv[67]/arm/} # modeled after electron.mk
25PLIST_SUB=	JS_ARCH=${JS_ARCH}
26
27OPTIONS_DEFINE=		AUDIO_CAPTURE USE_CURL_USER_AGENT
28OPTIONS_DEFAULT=	USE_CURL_USER_AGENT
29OPTIONS_SUB=		yes
30
31AUDIO_CAPTURE_DESC=		Build native audio capture module for BSD
32USE_CURL_USER_AGENT_DESC=	Use curl User-Agent for web fetching to bypass bot-blocking filters (e.g. Anubis)
33AUDIO_CAPTURE_BUILD_DEPENDS=	${LOCALBASE}/bin/cargo:lang/rust \
34				pkgconf:devel/pkgconf
35AUDIO_CAPTURE_LIB_DEPENDS=	libasound.so:audio/alsa-lib
36
37AUDIO_CAPTURE_COMMIT=		bb370f75235254da2c3d34f6168fc4a9902d9090
38
39PACKAGE_NAME=	@anthropic-ai/claude-code
40
41FETCH_SCRIPT=		${PORTSDIR}/Tools/scripts/npmjs-fetch-with-dependencies.sh
42AUDIO_CAPTURE_FETCH_SCRIPT=	${FILESDIR}/audio-capture-fetch.sh
43
44CLI_JS=			${WRKSRC}/node_modules/${PACKAGE_NAME}/cli.js
45AUDIO_CAPTURE_WRKSRC=	${WRKDIR}/audio-capture-${AUDIO_CAPTURE_COMMIT}
46
47.include <bsd.mport.options.mk>
48
49.if ${PORT_OPTIONS:MAUDIO_CAPTURE}
50DISTFILES+=	audio-capture-${AUDIO_CAPTURE_COMMIT}${EXTRACT_SUFX}:audio_capture_src
51.else
52NO_BUILD=	yes
53NO_ARCH=	yes
54.endif
55
56do-fetch:
57	@if ! [ -f ${DISTDIR}/${PORTNAME}-${DISTVERSION}${EXTRACT_SUFX} ]; then \
58		${SETENV} TMPDIR=${WRKDIR} LOCALBASE=${LOCALBASE} ${FETCH_SCRIPT} \
59			${PACKAGE_NAME} ${DISTVERSION} \
60			${FILESDIR}/package-lock.json \
61			${DISTDIR}/${PORTNAME}-${DISTVERSION}${EXTRACT_SUFX}; \
62	fi
63
64do-fetch-AUDIO_CAPTURE-on:
65	@if ! [ -f ${DISTDIR}/audio-capture-${AUDIO_CAPTURE_COMMIT}${EXTRACT_SUFX} ]; then \
66		${SETENV} TMPDIR=${WRKDIR} LOCALBASE=${LOCALBASE} \
67			${SH} ${AUDIO_CAPTURE_FETCH_SCRIPT} \
68			${AUDIO_CAPTURE_COMMIT} \
69			${DISTDIR}/audio-capture-${AUDIO_CAPTURE_COMMIT}${EXTRACT_SUFX}; \
70	fi
71
72# Raise API and MCP transport timeouts to 24 hours for slow (e.g. CPU-based) servers
73post-extract:
74	${REINPLACE_CMD} -i '' \
75		-e 's/var EP5=60000;/var EP5=86400000;/' \
76		-e 's/API_TIMEOUT_MS||String(600000)/API_TIMEOUT_MS||String(86400000)/' \
77		-e 's/DEFAULT_TIMEOUT=600000;/DEFAULT_TIMEOUT=86400000;/' \
78		-e 's#}async function Rt(){#}async function Rt(){if(process.argv[1]\&\&(process.argv[1].includes("/usr/local/lib/node_modules")||process.argv[1].includes("/usr/lib/node_modules")))return"package-manager";#' \
79		-e 's#f==="development"){E("AutoUpdater: Cannot auto-update development build")#f==="development"||f==="package-manager"){E("AutoUpdater: Cannot auto-update development build")#' \
80		${CLI_JS}
81
82post-extract-AUDIO_CAPTURE-on:
83	# patch cli.js to load audio-capture
84	${REINPLACE_CMD} -i '' \
85		-e 's|q!=="darwin"&&q!=="linux"&&q!=="win32"|q!=="darwin"\&\&q!=="linux"\&\&q!=="win32"\&\&q!=="midnightbsd"|' \
86		${CLI_JS}
87
88post-extract-USE_CURL_USER_AGENT-on:
89	# patch cli.js to use a curl-style User-Agent for web fetching
90	@_V=$$(${LOCALBASE}/bin/curl --version 2>/dev/null | head -1 | ${AWK} '{print $$2}'); \
91	${REINPLACE_CMD} -i '' \
92		-e 's|function dUq(){return`Claude-User ($${yA()}; +https://support.anthropic.com/)`}|function dUq(){return"curl/'"$$_V"'"}|' \
93		${CLI_JS}
94
95do-build:
96.if ${PORT_OPTIONS:MAUDIO_CAPTURE}
97	@${ECHO_MSG} "====> Building audio-capture for amd64..."
98	@cd ${AUDIO_CAPTURE_WRKSRC} && \
99		${SETENV} HOME=${WRKDIR} cargo build --release
100.endif
101
102do-install:
103	# install files
104	@${MKDIR} ${PREFIX}/lib
105	@cd ${WRKSRC} && \
106		${COPYTREE_SHARE} node_modules ${STAGEDIR}${PREFIX}/lib
107	# remove *.node binaries for FreeBSD platforms
108	@${FIND} ${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/vendor/audio-capture \
109		-name "*.node" -delete
110.if ${PORT_OPTIONS:MAUDIO_CAPTURE}
111	# install BSD amd64 audio-capture module
112	@${MKDIR} ${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/vendor/audio-capture/x64-freebsd
113	${INSTALL_LIB} ${AUDIO_CAPTURE_WRKSRC}/target/release/libaudio_capture.so \
114		${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/vendor/audio-capture/x64-freebsd/audio-capture.node
115.endif
116	# remove Linux-only seccomp binaries (seccomp is a Linux kernel API, guarded by process.platform==="linux" in cli.js)
117	@${RM} -r ${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/vendor/seccomp
118	# remove bundled ripgrep
119	@${RM} -r ${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/vendor/ripgrep
120	# remove empty vendor subdirectories (and vendor/ itself when AUDIO_CAPTURE=OFF)
121	@${FIND} ${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/vendor \
122		-depth -type d -empty -delete
123	# update shebang
124	${REINPLACE_CMD} -i '' \
125		-e "s|#!/usr/bin/env node|#!${TRUE_PREFIX}/bin/node|" \
126		${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/cli.js
127	# set exec bit
128	@${CHMOD} +x ${STAGEDIR}${PREFIX}/lib/node_modules/${PACKAGE_NAME}/cli.js
129	# create wrapper script that uses system ripgrep
130	@${MKDIR} ${STAGEDIR}${PREFIX}/bin
131	@${ECHO_CMD} '#!/bin/sh' > ${STAGEDIR}${PREFIX}/bin/claude
132	@${ECHO_CMD} 'export USE_BUILTIN_RIPGREP=false' >> ${STAGEDIR}${PREFIX}/bin/claude
133	@${ECHO_CMD} 'exec ${TRUE_PREFIX}/lib/node_modules/${PACKAGE_NAME}/cli.js "$$@"' >> ${STAGEDIR}${PREFIX}/bin/claude
134	@${CHMOD} +x ${STAGEDIR}${PREFIX}/bin/claude
135
136.include <bsd.port.mk>
137