Compare commits

..

15 Commits

Author SHA1 Message Date
Asher e950669f93 Use AgentAPI for additional Claude status reporting (#150)
Is it OK to add the flag like this or do we need to check the cli
version to determine whether the new flag is available? Or I could just
throw in an `||` to run the command again without the flag if it fails.

Blocked on adding AgentAPI to Claude.

Will need to do the same for Goose.
2025-06-17 19:14:42 +02:00
DevCats 3d78f5e262 Merge branch 'main' into claude-code-web 2025-06-16 20:02:40 -05:00
Hugo Dutka 1469373a50 uncomment the claude code app 2025-06-05 18:56:48 +02:00
Hugo Dutka 1551c17413 comment out the claude-code app temporarily 2025-06-05 18:53:36 +02:00
Hugo Dutka eac3e55537 add healthcheck to claude code web 2025-06-05 18:26:37 +02:00
Hugo Dutka c301da7e6b remove tee pipes - they make claude ignore actual terminal width and render 80 char lines 2025-06-04 19:03:41 +02:00
Hugo Dutka 7d64e7ea84 use agentapi attach 2025-06-04 17:53:07 +02:00
Hugo Dutka b5937c06a9 adjust agentapi terminal width and height 2025-06-04 15:41:33 +02:00
Hugo Dutka d2b91ae1a8 adjust agentapi terminal width and height 2025-06-04 14:32:27 +02:00
Hugo Dutka bd05d06a3b change claude code web app url 2025-06-04 13:19:05 +02:00
Ben Potter e340affe95 fix another typo 2025-06-03 18:36:50 -05:00
Ben Potter 16892d806e fix typo 2025-06-03 18:31:30 -05:00
Ben Potter 056f4b5a68 remove duplicate app 2025-06-03 18:29:28 -05:00
Ben Potter 1c99c57b6e add agentapi 2025-06-03 18:27:29 -05:00
BrunoQuaresma a0c1a051ed feat: add claude_code_web in claude-code 2025-06-03 20:54:58 +00:00
16 changed files with 408 additions and 793 deletions
-9
View File
@@ -190,15 +190,6 @@ main() {
done <<< "$modules"
# Always run formatter to ensure consistent formatting
echo "🔧 Running formatter to ensure consistent formatting..."
if command -v bun >/dev/null 2>&1; then
bun fmt >/dev/null 2>&1 || echo "⚠️ Warning: bun fmt failed, but continuing..."
else
echo "⚠️ Warning: bun not found, skipping formatting"
fi
echo ""
echo "📋 Summary:"
echo "Bump Type: $bump_type"
echo ""
-11
View File
@@ -25,17 +25,6 @@ jobs:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Set up Terraform
uses: coder/coder/.github/actions/setup-tf@main
- name: Install dependencies
run: bun install
- name: Extract bump type from label
id: bump-type
run: |
+268 -17
View File
@@ -1,17 +1,268 @@
<svg viewBox="0 0 106.14 115.53">
<path
d="M60.78,7.84c-4.36-2.52-9.73-2.52-14.08,0L14.44,26.47c-4.36,2.52-7.04,7.17-7.04,12.2v37.26c0,5.03,2.68,9.68,7.04,12.2l32.26,18.63c4.36,2.52,9.73,2.52,14.08,0l32.26-18.63c4.36-2.52,7.04-7.17,7.04-12.2v-37.26c0-5.03-2.68-9.68-7.04-12.2L60.78,7.84Z"
style="fill:url(#gradient)" />
<linearGradient id="gradient" x1="-228.54" y1="480.48" x2="-246.11" y2="446.65"
gradientTransform="translate(569.95 1056) scale(2.16 -2.16)"
gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2fabff" />
<stop offset=".31" stop-color="#5570ff" />
<stop offset=".62" stop-color="#7b36ff" />
<stop offset=".81" stop-color="#6a2cdc" />
<stop offset="1" stop-color="#5921b8" />
</linearGradient>
<path
d="M48.26,21.44l-22.88,13.21c-3.49,2.01-5.63,5.73-5.63,9.76v26.43c0,4.03,2.15,7.74,5.63,9.76l22.88,13.21c3.49,2.01,7.78,2.01,11.27,0l22.88-13.21c3.49-2.01,5.63-5.73,5.63-9.76v-26.43c0-4.03-2.15-7.74-5.63-9.76l-22.88-13.21c-3.49-2.01-7.78-2.01-11.27,0ZM51.78,27.7c1.74-1.01,3.89-1.01,5.63,0l21.81,12.59c1.74,1.01,2.82,2.86,2.82,4.88v25.19c0,2.01-1.07,3.87-2.82,4.88l-21.81,12.59c-1.74,1.01-3.89,1.01-5.63,0l-21.81-12.59c-1.74-1.01-2.82-2.86-2.82-4.88v-25.19c0-2.01,1.07-3.87,2.82-4.88l21.81-12.59ZM54.25,51.32c-.44-.25-.97-.25-1.41,0l-4.69,2.71c-.44.25-.7.72-.7,1.22v5.42c0,.5.27.97.7,1.22l4.69,2.71c.44.25.97.25,1.41,0l4.69-2.71c.44-.25.7-.72.7-1.22v-5.42c0-.5-.27-.97-.7-1.22l-4.69-2.71ZM57.77,56.55l-2.82,5.63,26.76,15.49,2.82-5.63-26.76-15.49Z"
style="fill:#fff; stroke:#fff; stroke-width:2.93px;" />
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg id="katman_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 841.9 595.3">
<!-- Generator: Adobe Illustrator 29.3.1, SVG Export Plug-In . SVG Version: 2.1.0 Build 151) -->
<defs>
<style>
.st0 {
fill: #2e3c4e;
}
.st1 {
opacity: 0;
}
.st1, .st2, .st3, .st4, .st5, .st6, .st7, .st8 {
display: none;
}
.st9 {
fill: #7300e5;
}
.st10, .st11 {
fill: #fff;
}
.st12 {
fill: url(#Adsız_degrade_4);
}
.st2 {
opacity: 0;
}
.st13 {
fill: url(#Adsız_degrade_41);
}
.st14 {
fill: #d7c8f9;
}
.st15 {
fill: none;
}
.st16 {
stroke: #d1d1d6;
stroke-width: .5px;
}
.st16, .st17, .st11 {
fill-opacity: 0;
}
.st3 {
opacity: 0;
}
.st17 {
stroke: #fff;
stroke-width: 4px;
}
.st4 {
opacity: 0;
}
.st5 {
opacity: 0;
}
.st7 {
opacity: 0;
}
.st18 {
fill: url(#Adsız_degrade_5);
}
.st8 {
opacity: 0;
}
.st19 {
fill: url(#Adsız_degrade_2);
}
</style>
<mask id="mask" x="69.2" y="33.9" width="704" height="528" maskUnits="userSpaceOnUse">
<g id="lottie-ymehjmywpqh__lottie_element_1058_2">
<g>
<rect class="st11" x="69.2" y="33.9" width="704" height="528"/>
<g class="st3">
<path class="st14" d="M324.5,185.8v33.5c0,4.4-3.6,8-8,8h-107.8c-4.4,0-8-3.6-8-8v-33.5c0-4.4,3.6-8,8-8h107.8c4.4,0,8,3.6,8,8Z"/>
</g>
</g>
</g>
</mask>
<linearGradient id="Adsız_degrade_2" data-name="Adsız degrade 2" x1="68.8" y1="563" x2="-1.8" y2="563.4" gradientTransform="translate(194.4 765.6) scale(1 -1)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff"/>
<stop offset=".5" stop-color="#fff" stop-opacity=".7"/>
<stop offset="1" stop-color="#fff" stop-opacity=".4"/>
</linearGradient>
<mask id="mask-1" x="69.2" y="33.9" width="704" height="528" maskUnits="userSpaceOnUse">
<g id="lottie-ymehjmywpqh__lottie_element_1038_2">
<g>
<rect class="st11" x="69.2" y="33.9" width="704" height="528"/>
<g class="st3">
<path d="M329.4,427.5v34c0,4.4-3.6,8-8,8h-127.2c-4.4.1-8-3.5-8-7.9v-34c0-4.4,3.6-8,8-8h127.2c4.4-.1,8,3.5,8,7.9Z"/>
</g>
</g>
</g>
</mask>
<linearGradient id="Adsız_degrade_4" data-name="Adsız degrade 4" x1="69.7" y1="567.5" x2="-11.4" y2="566.9" gradientTransform="translate(178.6 1012.1) scale(1 -1)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff"/>
<stop offset=".5" stop-color="#fff" stop-opacity=".6"/>
<stop offset="1" stop-color="#fff" stop-opacity=".2"/>
</linearGradient>
<mask id="mask-2" x="69.2" y="33.9" width="704" height="528" maskUnits="userSpaceOnUse">
<g id="lottie-ymehjmywpqh__lottie_element_1018_2">
<g>
<rect class="st11" x="69.2" y="33.9" width="704" height="528"/>
<g class="st3">
<path class="st9" d="M689.3,383.8v34.2c-.1,4.4-3.7,8-8.1,8h-150.3c-4.4,0-8-3.6-8-8v-34.2c.1-4.4,3.7-8,8.1-8h150.2c4.4,0,8,3.6,8,8Z"/>
</g>
</g>
</g>
</mask>
<linearGradient id="Adsız_degrade_41" data-name="Adsız degrade 4" x1="21.7" y1="568.3" x2="163.7" y2="568.3" gradientTransform="translate(551.4 969.2) scale(1 -1)" xlink:href="#Adsız_degrade_4"/>
<linearGradient id="Adsız_degrade_5" data-name="Adsız degrade 5" x1="56.8" y1="647.8" x2="-1.1" y2="536.3" gradientTransform="translate(349.3 2027.2) scale(3 -3)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2fabff"/>
<stop offset=".3" stop-color="#5570ff"/>
<stop offset=".6" stop-color="#7b36ff"/>
<stop offset=".8" stop-color="#6a2cdc"/>
<stop offset="1" stop-color="#5921b8"/>
</linearGradient>
</defs>
<rect class="st15" x="69.2" y="33.9" width="704" height="528"/>
<g class="st5">
<path class="st17" d="M424.4,138.3c-2.9-1.6-7.6-1.6-10.5,0l-130.4,69.8c-2.9,1.6-5.3,5.5-5.3,8.8v154.3c0,3.3,2.4,7.3,5.2,8.9l130.4,72.3c2.9,1.6,7.6,1.6,10.5,0l131.5-72.1c2.9-1.6,5.3-5.6,5.3-8.9v-151.8c0-3.3-2.4-7.3-5.3-8.9l-131.5-72.5Z"/>
</g>
<g class="st2">
<path class="st17" d="M424.4,138.3c-2.9-1.6-7.6-1.6-10.5,0l-130.4,69.8c-2.9,1.6-5.3,5.5-5.3,8.8v154.3c0,3.3,2.4,7.3,5.2,8.9l130.4,72.3c2.9,1.6,7.6,1.6,10.5,0l131.5-72.1c2.9-1.6,5.3-5.6,5.3-8.9v-151.8c0-3.3-2.4-7.3-5.3-8.9l-131.5-72.5Z"/>
</g>
<g class="st1">
<path class="st17" d="M424.4,138.3c-2.9-1.6-7.6-1.6-10.5,0l-130.4,69.8c-2.9,1.6-5.3,5.5-5.3,8.8v154.3c0,3.3,2.4,7.3,5.2,8.9l130.4,72.3c2.9,1.6,7.6,1.6,10.5,0l131.5-72.1c2.9-1.6,5.3-5.6,5.3-8.9v-151.8c0-3.3-2.4-7.3-5.3-8.9l-131.5-72.5Z"/>
</g>
<g class="st5">
<path class="st17" d="M424.4,138.3c-2.9-1.6-7.6-1.6-10.5,0l-130.4,69.8c-2.9,1.6-5.3,5.5-5.3,8.8v154.3c0,3.3,2.4,7.3,5.2,8.9l130.4,72.3c2.9,1.6,7.6,1.6,10.5,0l131.5-72.1c2.9-1.6,5.3-5.6,5.3-8.9v-151.8c0-3.3-2.4-7.3-5.3-8.9l-131.5-72.5Z"/>
</g>
<g class="st4">
<path class="st17" d="M424.4,139.4c-2.9-1.6-7.6-1.6-10.5,0l-129.6,69.3c-2.9,1.6-5.3,5.5-5.3,8.8v153.3c0,3.3,2.4,7.3,5.2,8.9l129.6,71.9c2.9,1.6,7.6,1.6,10.5,0l130.7-71.6c2.9-1.6,5.3-5.6,5.3-8.9v-150.9c0-3.3-2.4-7.3-5.3-8.9l-130.7-72Z"/>
</g>
<g class="st8">
<path class="st17" d="M424.4,157.4c-2.9-1.6-7.7-1.6-10.6,0l-115.9,61.1c-2.9,1.5-5.3,5.5-5.3,8.8v135.9c0,3.3,2.4,7.3,5.2,8.9l116,64.5c2.9,1.6,7.6,1.6,10.5,0l117.1-63.3c2.9-1.6,5.3-5.5,5.3-8.9v-135c0-3.3-2.4-7.3-5.3-8.8l-117.1-63.2Z"/>
</g>
<g class="st3">
<g>
<path class="st19" d="M324.5,185.8v33.5c0,4.4-3.6,8-8,8h-107.8c-4.4,0-8-3.6-8-8v-33.5c0-4.4,3.6-8,8-8h107.8c4.4,0,8,3.6,8,8Z"/>
<path class="st16" d="M324.5,185.8v33.5c0,4.4-3.6,8-8,8h-107.8c-4.4,0-8-3.6-8-8v-33.5c0-4.4,3.6-8,8-8h107.8c4.4,0,8,3.6,8,8Z"/>
</g>
</g>
<g class="st7">
<g class="st6">
<path class="st0" d="M236.1,209.6c.2,0,.3-.1.4-.2,0,0,0-.3,0-.5v-1.3c0-.3-.1-.5-.3-.5s-.2,0-.4,0c-.4,0-.8.2-1.3.2-.5,0-.9,0-1.2,0-1.4,0-2.4-.4-3.1-1.1s-1-1.8-1-3.3v-.5c0-1.5.3-2.6,1-3.4s1.7-1.1,3.1-1.1,1.4,0,2.2.3c.2,0,.4,0,.4,0,.2,0,.3-.1.3-.5v-1.3c0-.2,0-.4,0-.5s-.2-.2-.3-.2c-1-.3-1.9-.4-2.9-.4-2.2,0-3.9.6-5.1,1.9-1.2,1.3-1.8,3.1-1.8,5.4s.6,4.1,1.7,5.3c1.2,1.2,2.9,1.8,5.1,1.8s2.2-.1,3.2-.5Z"/>
</g>
<g class="st6">
<path class="st0" d="M240.4,204.7c0-2.1.7-3.2,2.1-3.2s2.1,1,2.1,3.2-.7,3.2-2.1,3.2-2.1-1.1-2.1-3.2ZM246.2,208.7c.9-.9,1.3-2.3,1.3-4s-.4-3-1.3-4c-.9-.9-2.1-1.4-3.7-1.4s-2.8.5-3.7,1.4c-.9.9-1.3,2.3-1.3,4s.5,3,1.3,4c.9.9,2.1,1.4,3.7,1.4s2.8-.5,3.7-1.4Z"/>
</g>
<g class="st6">
<path class="st0" d="M252.3,209.8c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-7.1c.7-.5,1.4-.7,2.2-.7s.9.1,1.1.4c.2.2.4.7.4,1.2v6.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-7.1c0-1-.3-1.7-.8-2.2-.5-.5-1.2-.8-2.2-.8-1.4,0-2.6.5-3.8,1.4l-.2-.7c0-.3-.2-.4-.6-.4h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v9.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M267.7,209.7c.2,0,.3-.1.3-.2,0,0,0-.2,0-.5v-1c0-.3-.1-.4-.3-.4s-.3,0-.5,0c-.2,0-.4,0-.6,0-.5,0-.9,0-1.1-.3-.2-.2-.3-.5-.3-.9v-4.8h2.3c.2,0,.3,0,.4-.1s.1-.2.1-.4v-1.2c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-2.3v-2.3c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.2.4l-.4,2.3-1.1.2c-.2,0-.3,0-.4.2,0,0-.1.2-.1.4v.8c0,.2,0,.3.1.4,0,0,.2.1.4.1h1v4.9c0,1.1.3,1.9.8,2.5.5.5,1.4.8,2.5.8s1.4,0,2-.3Z"/>
</g>
<g class="st6">
<path class="st0" d="M272.5,203.6c0-.8.3-1.3.7-1.7.4-.4.9-.6,1.6-.6,1.1,0,1.7.7,1.7,2v.3h-3.9ZM278.3,209.6c.2,0,.3-.1.4-.2,0,0,0-.2,0-.4v-.9c0-.3-.1-.5-.3-.5s0,0-.1,0h-.1c-1,.3-1.8.4-2.6.4s-1.8-.2-2.3-.6-.8-1-.8-1.9h5.8c.2,0,.3,0,.4,0s.1-.2.2-.4c0-.5,0-1,0-1.4,0-1.3-.4-2.4-1.1-3.1-.7-.7-1.7-1.1-3-1.1s-2.8.5-3.7,1.4c-.9,1-1.3,2.3-1.3,4s.4,3.1,1.3,4c.9.9,2.2,1.4,3.9,1.4s2.2-.2,3.2-.6Z"/>
</g>
<g class="st6">
<path class="st0" d="M283.6,209.8c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-7.1c.7-.5,1.4-.7,2.2-.7s.9.1,1.1.4c.2.2.4.7.4,1.2v6.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-7.1c0-1-.3-1.7-.8-2.2-.5-.5-1.2-.8-2.2-.8-1.4,0-2.6.5-3.8,1.4l-.2-.7c0-.3-.2-.4-.6-.4h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v9.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M299.1,209.7c.2,0,.3-.1.3-.2,0,0,0-.2,0-.5v-1c0-.3-.1-.4-.3-.4s-.3,0-.5,0c-.2,0-.4,0-.6,0-.5,0-.9,0-1.1-.3-.2-.2-.3-.5-.3-.9v-4.8h2.3c.2,0,.3,0,.4-.1s.1-.2.1-.4v-1.2c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-2.3v-2.3c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.2.4l-.4,2.3-1.1.2c-.2,0-.3,0-.4.2,0,0-.1.2-.1.4v.8c0,.2,0,.3.1.4,0,0,.2.1.4.1h1v4.9c0,1.1.3,1.9.8,2.5.5.5,1.4.8,2.5.8s1.4,0,2-.3Z"/>
</g>
</g>
<g class="st3">
<g>
<path class="st12" d="M329.4,427.5v34c0,4.4-3.6,8-8,8h-127.2c-4.4.1-8-3.5-8-7.9v-34c0-4.4,3.6-8,8-8h127.2c4.4-.1,8,3.5,8,7.9Z"/>
<path class="st16" d="M329.4,427.5v34c0,4.4-3.6,8-8,8h-127.2c-4.4.1-8-3.5-8-7.9v-34c0-4.4,3.6-8,8-8h127.2c4.4-.1,8,3.5,8,7.9Z"/>
</g>
</g>
<g class="st7">
<g class="st6">
<path class="st0" d="M213.9,439.7h1.7c2.4,0,3.6,1.5,3.6,4.4v.4c0,2.9-1.2,4.4-3.6,4.4h-1.7v-9.2ZM215.9,451.2c2,0,3.6-.6,4.7-1.8,1.1-1.2,1.7-2.9,1.7-5.1s-.6-3.9-1.7-5.1c-1.1-1.2-2.7-1.8-4.8-1.8h-4.5c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v12.9c0,.2,0,.3.1.4,0,0,.2.1.4.1h4.5Z"/>
</g>
<g class="st6">
<path class="st0" d="M227.4,449.3c-.2-.2-.3-.5-.3-1,0-.9.6-1.4,1.7-1.4s1,0,1.6.1v1.7c-.3.2-.6.5-1,.6s-.7.2-1,.2-.7-.1-1-.3ZM229.1,451.2c.5-.2,1-.5,1.4-.9v.5c.2.2.2.3.3.3,0,0,.2.1.4.1h1.4c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-6.6c0-1.2-.3-2.1-1-2.6-.6-.6-1.6-.8-3-.8s-1.3,0-2,.2c-.7.1-1.2.3-1.7.5-.2,0-.3.2-.4.2,0,0,0,.2,0,.4v.9c0,.3.1.5.3.5s0,0,.1,0c0,0,0,0,.1,0,1.1-.3,2.2-.5,3.1-.5s1.2.1,1.4.3c.2.2.4.7.4,1.3v1c-.8-.2-1.5-.3-2.1-.3s-1.5.1-2.1.4c-.6.3-1.1.6-1.4,1.1-.3.5-.5,1.1-.5,1.7,0,.9.3,1.7.9,2.2.6.6,1.4.8,2.4.8s1-.1,1.6-.3Z"/>
</g>
<g class="st6">
<path class="st0" d="M241.5,451.1c.2,0,.3-.1.3-.2,0,0,0-.2,0-.5v-1c0-.3-.1-.4-.3-.4s-.3,0-.5,0c-.2,0-.4,0-.6,0-.5,0-.9,0-1.1-.3-.2-.2-.3-.5-.3-.9v-4.8h2.3c.2,0,.3,0,.4-.1s.1-.2.1-.4v-1.2c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-2.3v-2.3c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.2.4l-.4,2.3-1.1.2c-.2,0-.3,0-.4.2,0,0-.1.2-.1.4v.8c0,.2,0,.3.1.4,0,0,.2.1.4.1h1v4.9c0,1.1.3,1.9.8,2.5.5.5,1.4.8,2.5.8s1.4,0,2-.3Z"/>
</g>
<g class="st6">
<path class="st0" d="M246.6,449.3c-.2-.2-.3-.5-.3-1,0-.9.6-1.4,1.7-1.4s1,0,1.6.1v1.7c-.3.2-.6.5-1,.6s-.7.2-1,.2-.7-.1-1-.3ZM248.3,451.2c.5-.2,1-.5,1.4-.9v.5c.2.2.2.3.3.3,0,0,.2.1.4.1h1.4c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-6.6c0-1.2-.3-2.1-1-2.6-.6-.6-1.6-.8-3-.8s-1.3,0-2,.2c-.7.1-1.2.3-1.7.5-.2,0-.3.2-.4.2,0,0,0,.2,0,.4v.9c0,.3.1.5.3.5s0,0,.1,0c0,0,0,0,.1,0,1.1-.3,2.2-.5,3.1-.5s1.2.1,1.4.3c.2.2.4.7.4,1.3v1c-.8-.2-1.5-.3-2.1-.3s-1.5.1-2.1.4c-.6.3-1.1.6-1.4,1.1-.3.5-.5,1.1-.5,1.7,0,.9.3,1.7.9,2.2.6.6,1.4.8,2.4.8s1-.1,1.6-.3Z"/>
</g>
<g class="st6">
<path class="st0" d="M263,450.8c.6-.4,1.1-1.1,1.5-1.9.4-.8.6-1.8.6-2.9,0-1.6-.4-2.9-1.1-3.8-.8-.9-1.8-1.4-3.1-1.4s-1,.1-1.5.3c-.5.2-1,.5-1.4.8v-4.9c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.9c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v13.8c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.5c.3,0,.5-.2.6-.4v-.6c.5.4.9.7,1.5.9.5.2,1.1.3,1.7.3s1.6-.2,2.2-.7ZM257.9,448.7v-5.3c.6-.4,1.3-.5,2-.5s1.3.2,1.7.8.5,1.3.5,2.4-.2,1.9-.5,2.4-.9.8-1.6.8-1.4-.2-2-.5Z"/>
</g>
<g class="st6">
<path class="st0" d="M269.8,449.3c-.2-.2-.3-.5-.3-1,0-.9.6-1.4,1.7-1.4s1,0,1.6.1v1.7c-.3.2-.6.5-1,.6s-.7.2-1,.2-.7-.1-1-.3ZM271.5,451.2c.5-.2,1-.5,1.4-.9v.5c.2.2.2.3.3.3,0,0,.2.1.4.1h1.4c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-6.6c0-1.2-.3-2.1-1-2.6-.6-.6-1.6-.8-3-.8s-1.3,0-2,.2c-.7.1-1.2.3-1.7.5-.2,0-.3.2-.4.2,0,0,0,.2,0,.4v.9c0,.3.1.5.3.5s0,0,.1,0c0,0,0,0,.1,0,1.1-.3,2.2-.5,3.1-.5s1.2.1,1.4.3c.2.2.4.7.4,1.3v1c-.8-.2-1.5-.3-2.1-.3s-1.5.1-2.1.4c-.6.3-1.1.6-1.4,1.1-.3.5-.5,1.1-.5,1.7,0,.9.3,1.7.9,2.2.6.6,1.4.8,2.4.8s1-.1,1.6-.3Z"/>
</g>
<g class="st6">
<path class="st0" d="M284.1,450.7c.8-.6,1.1-1.4,1.1-2.4s-.2-1.3-.5-1.7c-.3-.4-.9-.8-1.8-1.2l-1.5-.6c-.5-.2-.8-.4-1-.5-.2-.1-.2-.3-.2-.6s.1-.5.4-.6c.3-.1.6-.2,1.2-.2s1.4,0,1.9.2c.3,0,.5.1.6.1.2,0,.3-.1.3-.5v-.9c0-.2,0-.4,0-.5,0,0-.2-.2-.3-.2-.8-.3-1.7-.5-2.7-.5s-2.2.3-2.9.8c-.7.5-1.1,1.3-1.1,2.2s.2,1.3.5,1.8c.4.5,1,.9,1.8,1.2l1.6.6c.5.2.8.3.9.5.1.2.2.4.2.6,0,.7-.5,1-1.4,1s-1,0-1.5-.1c-.5,0-1-.2-1.5-.3-.1,0-.2,0-.3,0-.2,0-.3.1-.3.5v.9c0,.2,0,.3,0,.4,0,0,.2.2.4.2.9.4,2,.6,3.2.6s2.2-.3,3-.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M289.3,445c0-.8.3-1.3.7-1.7.4-.4.9-.6,1.6-.6,1.1,0,1.7.7,1.7,2v.3h-3.9ZM295.1,451c.2,0,.3-.1.4-.2,0,0,0-.2,0-.4v-.9c0-.3-.1-.5-.3-.5s0,0-.1,0h-.1c-1,.3-1.8.4-2.6.4s-1.8-.2-2.3-.6-.8-1-.8-1.9h5.8c.2,0,.3,0,.4,0s.1-.2.2-.4c0-.5,0-1,0-1.4,0-1.3-.4-2.4-1.1-3.1-.7-.7-1.7-1.1-3-1.1s-2.8.5-3.7,1.4c-.9,1-1.3,2.3-1.3,4s.4,3.1,1.3,4c.9.9,2.2,1.4,3.9,1.4s2.2-.2,3.2-.6Z"/>
</g>
<g class="st6">
<path class="st0" d="M303.8,450.7c.8-.6,1.1-1.4,1.1-2.4s-.2-1.3-.5-1.7c-.3-.4-.9-.8-1.8-1.2l-1.5-.6c-.5-.2-.8-.4-1-.5-.2-.1-.2-.3-.2-.6s.1-.5.4-.6c.3-.1.6-.2,1.2-.2s1.4,0,1.9.2c.3,0,.5.1.6.1.2,0,.3-.1.3-.5v-.9c0-.2,0-.4,0-.5,0,0-.2-.2-.3-.2-.8-.3-1.7-.5-2.7-.5s-2.2.3-2.9.8c-.7.5-1.1,1.3-1.1,2.2s.2,1.3.5,1.8c.4.5,1,.9,1.8,1.2l1.6.6c.5.2.8.3.9.5.1.2.2.4.2.6,0,.7-.5,1-1.4,1s-1,0-1.5-.1c-.5,0-1-.2-1.5-.3-.1,0-.2,0-.3,0-.2,0-.3.1-.3.5v.9c0,.2,0,.3,0,.4,0,0,.2.2.4.2.9.4,2,.6,3.2.6s2.2-.3,3-.9Z"/>
</g>
</g>
<g class="st3">
<g>
<path class="st13" d="M689.3,383.8v34.2c-.1,4.4-3.7,8-8.1,8h-150.3c-4.4,0-8-3.6-8-8v-34.2c.1-4.4,3.7-8,8.1-8h150.2c4.4,0,8,3.6,8,8Z"/>
<path class="st16" d="M689.3,383.8v34.2c-.1,4.4-3.7,8-8.1,8h-150.3c-4.4,0-8-3.6-8-8v-34.2c.1-4.4,3.7-8,8.1-8h150.2c4.4,0,8,3.6,8,8Z"/>
</g>
</g>
<g class="st7">
<g class="st6">
<path class="st0" d="M549.5,407.8c.2,0,.4,0,.5-.1,0,0,.2-.2.2-.4l.8-2.6h4.8l.8,2.6c0,.2.2.3.2.4.1,0,.2.1.4.1h2.2c.3,0,.4-.1.4-.3s0-.1,0-.2c0,0,0-.2-.1-.3l-4.4-12.5c0-.2-.1-.3-.2-.4,0,0-.3-.1-.5-.1h-2.2c-.2,0-.3,0-.3,0,0,0-.1,0-.2.2,0,0-.1.2-.2.3l-4.4,12.5c0,.1,0,.2-.1.3,0,.1,0,.2,0,.2,0,.2.1.3.4.3h2.1ZM551.7,402.5l1.7-5.8,1.8,5.8h-3.5Z"/>
</g>
<g class="st6">
<path class="st0" d="M564.9,405.3v-5.3c.6-.4,1.3-.5,2-.5s1.3.2,1.7.8.5,1.3.5,2.4-.2,1.9-.5,2.4-.9.8-1.6.8-1.4-.2-2-.5ZM564.4,412c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-4.4c.3.3.8.5,1.3.7.5.2,1,.3,1.6.3.8,0,1.6-.2,2.2-.7.6-.4,1.1-1.1,1.5-1.9.4-.8.6-1.8.6-2.9,0-1.6-.4-2.9-1.1-3.8-.8-.9-1.8-1.4-3.1-1.4s-1.2.1-1.7.3c-.6.2-1,.5-1.4.9v-.5c-.2-.3-.4-.4-.7-.4h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v13.3c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M577.2,405.3v-5.3c.6-.4,1.3-.5,2-.5s1.3.2,1.7.8.5,1.3.5,2.4-.2,1.9-.5,2.4-.9.8-1.6.8-1.4-.2-2-.5ZM576.7,412c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-4.4c.3.3.8.5,1.3.7.5.2,1,.3,1.6.3.8,0,1.6-.2,2.2-.7.6-.4,1.1-1.1,1.5-1.9.4-.8.6-1.8.6-2.9,0-1.6-.4-2.9-1.1-3.8-.8-.9-1.8-1.4-3.1-1.4s-1.2.1-1.7.3c-.6.2-1,.5-1.4.9v-.5c-.2-.3-.4-.4-.7-.4h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v13.3c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M590.6,407.8c.2,0,.3-.1.4-.2,0,0,0-.2,0-.5v-1c0-.2,0-.3,0-.3,0,0-.1-.1-.3-.1s-.1,0-.2,0c0,0-.2,0-.3,0-.5,0-.7-.3-.7-1v-11.2c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.9c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v11.4c0,2,.9,3,2.7,3s1,0,1.4-.2Z"/>
</g>
<g class="st6">
<path class="st0" d="M595.8,395.6c.3-.3.5-.7.5-1.1s-.2-.9-.5-1.1-.7-.4-1.2-.4-.9.1-1.2.4c-.3.3-.5.7-.5,1.1s.1.9.5,1.1c.3.3.7.4,1.2.4s.9-.1,1.2-.4ZM595.5,407.8c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-9.2c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.9c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v9.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M604.6,408c.4,0,.8-.2,1.2-.3.2,0,.3-.1.3-.2,0,0,0-.2,0-.4v-1c0-.3-.1-.4-.3-.4s-.2,0-.3,0c-.6.2-1.1.2-1.6.2-.9,0-1.6-.2-2-.7-.4-.5-.6-1.2-.6-2.2v-.3c0-1,.2-1.8.7-2.2.4-.5,1.1-.7,2.1-.7s1,0,1.6.2c0,0,0,0,.1,0,0,0,0,0,.1,0,.2,0,.3-.2.3-.5v-.9c0-.2,0-.4,0-.5,0,0-.2-.2-.4-.2-.7-.3-1.5-.4-2.3-.4-1.6,0-2.9.5-3.8,1.4-.9,1-1.4,2.3-1.4,4s.4,3,1.3,3.9c.9.9,2.1,1.4,3.7,1.4s.8,0,1.2-.1Z"/>
</g>
<g class="st6">
<path class="st0" d="M610.7,405.9c-.2-.2-.3-.5-.3-1,0-.9.6-1.4,1.7-1.4s1,0,1.6.1v1.7c-.3.2-.6.5-1,.6s-.7.2-1,.2-.7-.1-1-.3ZM612.4,407.8c.5-.2,1-.5,1.4-.9v.5c.2.2.2.3.3.3,0,0,.2.1.4.1h1.4c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-6.6c0-1.2-.3-2.1-1-2.6-.6-.6-1.6-.8-3-.8s-1.3,0-2,.2c-.7.1-1.2.3-1.7.5-.2,0-.3.2-.4.2,0,0,0,.2,0,.4v.9c0,.3.1.5.3.5s0,0,.1,0c0,0,0,0,.1,0,1.1-.3,2.2-.5,3.1-.5s1.2.1,1.4.3c.2.2.4.7.4,1.3v1c-.8-.2-1.5-.3-2.1-.3s-1.5.1-2.1.4c-.6.3-1.1.6-1.4,1.1-.3.5-.5,1.1-.5,1.7,0,.9.3,1.7.9,2.2.6.6,1.4.8,2.4.8s1-.1,1.6-.3Z"/>
</g>
<g class="st6">
<path class="st0" d="M624.7,407.7c.2,0,.3-.1.3-.2,0,0,0-.2,0-.5v-1c0-.3-.1-.4-.3-.4s-.3,0-.5,0c-.2,0-.4,0-.6,0-.5,0-.9,0-1.1-.3-.2-.2-.3-.5-.3-.9v-4.8h2.3c.2,0,.3,0,.4-.1s.1-.2.1-.4v-1.2c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-2.3v-2.3c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.2.4l-.4,2.3-1.1.2c-.2,0-.3,0-.4.2,0,0-.1.2-.1.4v.8c0,.2,0,.3.1.4,0,0,.2.1.4.1h1v4.9c0,1.1.3,1.9.8,2.5.5.5,1.4.8,2.5.8s1.4,0,2-.3Z"/>
</g>
<g class="st6">
<path class="st0" d="M630.1,395.6c.3-.3.5-.7.5-1.1s-.2-.9-.5-1.1-.7-.4-1.2-.4-.9.1-1.2.4c-.3.3-.5.7-.5,1.1s.1.9.5,1.1c.3.3.7.4,1.2.4s.9-.1,1.2-.4ZM629.9,407.8c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-9.2c0-.2,0-.3-.1-.4,0,0-.2-.1-.4-.1h-1.9c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v9.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M635.7,402.7c0-2.1.7-3.2,2.1-3.2s2.1,1,2.1,3.2-.7,3.2-2.1,3.2-2.1-1.1-2.1-3.2ZM641.5,406.7c.9-.9,1.3-2.3,1.3-4s-.4-3-1.3-4c-.9-.9-2.1-1.4-3.7-1.4s-2.8.5-3.7,1.4c-.9.9-1.3,2.3-1.3,4s.5,3,1.3,4c.9.9,2.1,1.4,3.7,1.4s2.8-.5,3.7-1.4Z"/>
</g>
<g class="st6">
<path class="st0" d="M647.6,407.8c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-7.1c.7-.5,1.4-.7,2.2-.7s.9.1,1.1.4c.2.2.4.7.4,1.2v6.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9c.2,0,.3,0,.4-.1,0,0,.1-.2.1-.4v-7.1c0-1-.3-1.7-.8-2.2-.5-.5-1.2-.8-2.2-.8-1.4,0-2.6.5-3.8,1.4l-.2-.7c0-.3-.2-.4-.6-.4h-1.4c-.2,0-.3,0-.4.1,0,0-.1.2-.1.4v9.2c0,.2,0,.3.1.4,0,0,.2.1.4.1h1.9Z"/>
</g>
<g class="st6">
<path class="st0" d="M663.3,407.3c.8-.6,1.1-1.4,1.1-2.4s-.2-1.3-.5-1.7c-.3-.4-.9-.8-1.8-1.2l-1.5-.6c-.5-.2-.8-.4-1-.5-.2-.1-.2-.3-.2-.6s.1-.5.4-.6c.3-.1.6-.2,1.2-.2s1.4,0,1.9.2c.3,0,.5.1.6.1.2,0,.3-.1.3-.5v-.9c0-.2,0-.4,0-.5,0,0-.2-.2-.3-.2-.8-.3-1.7-.5-2.7-.5s-2.2.3-2.9.8c-.7.5-1.1,1.3-1.1,2.2s.2,1.3.5,1.8c.4.5,1,.9,1.8,1.2l1.6.6c.5.2.8.3.9.5.1.2.2.4.2.6,0,.7-.5,1-1.4,1s-1,0-1.5-.1c-.5,0-1-.2-1.5-.3-.1,0-.2,0-.3,0-.2,0-.3.1-.3.5v.9c0,.2,0,.3,0,.4,0,0,.2.2.4.2.9.4,2,.6,3.2.6s2.2-.3,3-.9Z"/>
</g>
</g>
<g>
<path class="st18" d="M452.1,74c-19.6-11.3-43.8-11.3-63.4,0l-145.3,83.9c-19.6,11.3-31.7,32.3-31.7,54.9v167.8c0,22.7,12.1,43.6,31.7,54.9l145.3,83.9c19.6,11.3,43.8,11.3,63.4,0l145.3-83.9c19.6-11.3,31.7-32.3,31.7-54.9v-167.8c0-22.7-12.1-43.6-31.7-54.9l-145.3-83.9Z"/>
<path class="st10" d="M438.6,293.4l-12.7,25.4,120.5,69.8,12.7-25.4-120.5-69.8ZM422.7,269.8c-2-1.1-4.4-1.1-6.3,0l-21.1,12.2c-2,1.1-3.2,3.2-3.2,5.5v24.4c0,2.3,1.2,4.4,3.2,5.5l21.1,12.2c2,1.1,4.4,1.1,6.3,0l21.1-12.2c2-1.1,3.2-3.2,3.2-5.5v-24.4c0-2.3-1.2-4.4-3.2-5.5l-21.1-12.2ZM411.6,163.4c7.9-4.5,17.5-4.5,25.4,0l98.2,56.7c7.9,4.5,12.7,12.9,12.7,22v113.4c0,9.1-4.8,17.4-12.7,22l-98.2,56.7c-7.9,4.5-17.5,4.5-25.4,0l-98.2-56.7c-7.9-4.5-12.7-12.9-12.7-22v-113.4c0-9.1,4.8-17.4,12.7-22l98.2-56.7ZM395.7,135.2l-103.1,59.5c-15.7,9.1-25.4,25.8-25.4,44v119c0,18.1,9.7,34.9,25.4,44l103.1,59.5c15.7,9.1,35,9.1,50.8,0l103.1-59.5c15.7-9.1,25.4-25.8,25.4-44v-119c0-18.1-9.7-34.9-25.4-44l-103.1-59.5c-15.7-9.1-35-9.1-50.8,0Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 20 KiB

+6 -10
View File
@@ -216,13 +216,6 @@ EOT
model_flag = var.ai_provider == "ollama" ? "--ollama-model" : "--model"
}
# Set environment variable for AI provider API key
resource "coder_env" "ai_api_key" {
agent_id = var.agent_id
name = local.env_var_name
value = var.ai_api_key
}
# Install and Initialize Aider
resource "coder_script" "aider" {
agent_id = var.agent_id
@@ -394,7 +387,7 @@ EOL
fi
echo "Starting Aider using ${var.ai_provider} provider and model: ${var.ai_model}"
tmux new-session -d -s ${var.session_name} -c ${var.folder} "aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\""
tmux new-session -d -s ${var.session_name} -c ${var.folder} "export ${local.env_var_name}=\"${var.ai_api_key}\"; aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\""
echo "Aider task started in tmux session '${var.session_name}'. Check the UI for progress."
else
# Configure tmux for shared sessions
@@ -409,7 +402,7 @@ EOL
fi
echo "Starting Aider using ${var.ai_provider} provider and model: ${var.ai_model}"
tmux new-session -d -s ${var.session_name} -c ${var.folder} "aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${var.system_prompt}\""
tmux new-session -d -s ${var.session_name} -c ${var.folder} "export ${local.env_var_name}=\"${var.ai_api_key}\"; aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${var.system_prompt}\""
echo "Tmux session '${var.session_name}' started. Access it by clicking the Aider button."
fi
else
@@ -435,6 +428,7 @@ EOL
screen -U -dmS ${var.session_name} bash -c "
cd ${var.folder}
export PATH=\"$HOME/bin:$HOME/.local/bin:$PATH\"
export ${local.env_var_name}=\"${var.ai_api_key}\"
aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\"
/bin/bash
"
@@ -461,6 +455,7 @@ EOL
screen -U -dmS ${var.session_name} bash -c "
cd ${var.folder}
export PATH=\"$HOME/bin:$HOME/.local/bin:$PATH\"
export ${local.env_var_name}=\"${var.ai_api_key}\"
aider --architect --yes-always ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\"
/bin/bash
"
@@ -494,7 +489,7 @@ resource "coder_app" "aider_cli" {
tmux attach-session -t ${var.session_name}
else
echo "Starting new Aider tmux session..."
tmux new-session -s ${var.session_name} -c ${var.folder} "aider ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\"; exec bash"
tmux new-session -s ${var.session_name} -c ${var.folder} "export ${local.env_var_name}=\"${var.ai_api_key}\"; aider ${local.model_flag} ${var.ai_model} --message \"${local.combined_prompt}\"; exec bash"
fi
elif [ "${var.use_screen}" = "true" ]; then
if ! screen -list | grep -q "${var.session_name}"; then
@@ -505,6 +500,7 @@ resource "coder_app" "aider_cli" {
else
cd "${var.folder}"
echo "Starting Aider directly..."
export ${local.env_var_name}="${var.ai_api_key}"
aider ${local.model_flag} ${var.ai_model} --message "${local.combined_prompt}"
fi
EOT
+19
View File
@@ -114,6 +114,25 @@ module "amazon-q" {
}
```
## Variables
| Name | Required | Default | Description |
| -------------------------------- | -------- | ------------------------ | ----------------------------------------------------------------------------------------------- |
| `agent_id` | Yes | — | The ID of a Coder agent. |
| `experiment_auth_tarball` | Yes | — | Base64-encoded, zstd-compressed tarball of a pre-authenticated Amazon Q config directory. |
| `install_amazon_q` | No | `true` | Whether to install Amazon Q. |
| `amazon_q_version` | No | `latest` | Version to install. |
| `experiment_use_screen` | No | `false` | Use GNU screen for background operation. |
| `experiment_use_tmux` | No | `false` | Use tmux for background operation. |
| `experiment_report_tasks` | No | `false` | Enable task reporting to Coder. |
| `experiment_pre_install_script` | No | `null` | Custom script to run before install. |
| `experiment_post_install_script` | No | `null` | Custom script to run after install. |
| `icon` | No | `/icon/amazon-q.svg` | The icon to use for the app. |
| `folder` | No | `/home/coder` | The folder to run Amazon Q in. |
| `order` | No | `null` | The order determines the position of app in the UI presentation. |
| `system_prompt` | No | See [main.tf](./main.tf) | The system prompt to use for Amazon Q. This should instruct the agent how to do task reporting. |
| `ai_prompt` | No | See [main.tf](./main.tf) | The initial task prompt to send to Amazon Q. |
## Notes
- Only one of `experiment_use_screen` or `experiment_use_tmux` can be true at a time.
+16 -33
View File
@@ -14,7 +14,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "1.4.0"
version = "1.3.1"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
@@ -72,51 +72,34 @@ data "coder_parameter" "ai_prompt" {
mutable = true
}
# Set the prompt and system prompt for Claude Code via environment variables
resource "coder_agent" "main" {
# ...
env = {
CODER_MCP_CLAUDE_API_KEY = var.anthropic_api_key # or use a coder_parameter
CODER_MCP_CLAUDE_TASK_PROMPT = data.coder_parameter.ai_prompt.value
CODER_MCP_APP_STATUS_SLUG = "claude-code"
CODER_MCP_CLAUDE_SYSTEM_PROMPT = <<-EOT
You are a helpful assistant that can help with code.
EOT
}
}
module "claude-code" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/claude-code/coder"
version = "1.4.0"
version = "1.3.1"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
claude_code_version = "0.2.57"
# Set API key and prompts directly in the module
claude_api_key = var.anthropic_api_key # or use a coder_parameter
task_prompt = data.coder_parameter.ai_prompt.value
system_prompt = <<-EOT
You are a helpful assistant that can help with code.
EOT
# Enable experimental features
experiment_use_screen = true # Or use experiment_use_tmux = true to use tmux instead
experiment_report_tasks = true
}
```
## Session Persistence (Experimental)
Enable automatic session persistence to maintain Claude Code sessions across workspace restarts:
```tf
module "claude-code" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/claude-code/coder"
version = "1.4.0"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
# Enable tmux with session persistence
experiment_use_tmux = true
experiment_tmux_session_persistence = true
experiment_tmux_session_save_interval = "10" # Save every 10 minutes
experiment_report_tasks = true
}
```
Session persistence automatically saves and restores your Claude Code environment, including working directory and command history.
## Run standalone
Run Claude Code as a standalone app in your workspace. This will install Claude Code and run it directly without using screen or any task reporting to the Coder UI.
@@ -124,7 +107,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "1.4.0"
version = "1.3.1"
agent_id = coder_agent.example.id
folder = "/home/coder"
install_claude_code = true
+71 -180
View File
@@ -84,76 +84,11 @@ variable "experiment_post_install_script" {
default = null
}
variable "experiment_tmux_session_persistence" {
type = bool
description = "Whether to enable tmux session persistence across workspace restarts."
default = false
}
variable "experiment_tmux_session_save_interval" {
type = string
description = "How often to save tmux sessions in minutes."
default = "15"
}
variable "claude_api_key" {
type = string
description = "Anthropic API key for Claude."
default = ""
sensitive = true
}
variable "task_prompt" {
type = string
description = "Task prompt for Claude Code."
default = ""
}
variable "system_prompt" {
type = string
description = "System prompt for Claude Code."
default = ""
}
variable "app_status_slug" {
type = string
description = "App status slug for Claude Code."
default = "claude-code"
}
locals {
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
}
# Set environment variables for Claude Code
resource "coder_env" "claude_api_key" {
count = var.claude_api_key != "" ? 1 : 0
agent_id = var.agent_id
name = "CODER_MCP_CLAUDE_API_KEY"
value = var.claude_api_key
}
resource "coder_env" "claude_task_prompt" {
count = var.task_prompt != "" ? 1 : 0
agent_id = var.agent_id
name = "CODER_MCP_CLAUDE_TASK_PROMPT"
value = var.task_prompt
}
resource "coder_env" "claude_system_prompt" {
count = var.system_prompt != "" ? 1 : 0
agent_id = var.agent_id
name = "CODER_MCP_CLAUDE_SYSTEM_PROMPT"
value = var.system_prompt
}
resource "coder_env" "claude_app_status_slug" {
agent_id = var.agent_id
name = "CODER_MCP_APP_STATUS_SLUG"
value = var.app_status_slug
}
# Install and Initialize Claude Code
resource "coder_script" "claude_code" {
agent_id = var.agent_id
@@ -163,28 +98,12 @@ resource "coder_script" "claude_code" {
#!/bin/bash
set -e
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
install_tmux() {
echo "Installing tmux..."
if command_exists apt-get; then
sudo apt-get update && sudo apt-get install -y tmux
elif command_exists yum; then
sudo yum install -y tmux
elif command_exists dnf; then
sudo dnf install -y tmux
elif command_exists pacman; then
sudo pacman -S --noconfirm tmux
elif command_exists apk; then
sudo apk add tmux
else
echo "Error: Unable to install tmux automatically. Package manager not recognized."
exit 1
fi
}
# Check if the specified folder exists
if [ ! -d "${var.folder}" ]; then
echo "Warning: The specified folder '${var.folder}' does not exist."
echo "Creating the folder..."
@@ -193,6 +112,8 @@ resource "coder_script" "claude_code" {
mkdir -p "${var.folder}"
echo "Folder created successfully."
fi
# Run pre-install script if provided
if [ -n "${local.encoded_pre_install_script}" ]; then
echo "Running pre-install script..."
echo "${local.encoded_pre_install_script}" | base64 -d > /tmp/pre_install.sh
@@ -200,40 +121,27 @@ resource "coder_script" "claude_code" {
/tmp/pre_install.sh
fi
# Install Claude Code if enabled
if [ "${var.install_claude_code}" = "true" ]; then
if ! command_exists npm; then
echo "npm not found, checking for Node.js installation..."
if ! command_exists node; then
echo "Node.js not found, installing Node.js via NVM..."
export NVM_DIR="$HOME/.nvm"
if [ ! -d "$NVM_DIR" ]; then
mkdir -p "$NVM_DIR"
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
else
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
fi
nvm install --lts
nvm use --lts
nvm alias default node
echo "Node.js installed: $(node --version)"
echo "npm installed: $(npm --version)"
else
echo "Node.js is installed but npm is not available. Please install npm manually."
exit 1
fi
echo "Error: npm is not installed. Please install Node.js and npm first."
exit 1
fi
echo "Installing Claude Code..."
npm install -g @anthropic-ai/claude-code@${var.claude_code_version}
fi
# Hardcoded for now: install AgentAPI
wget https://github.com/coder/agentapi/releases/download/preview/agentapi-linux-amd64
chmod +x agentapi-linux-amd64
sudo mv agentapi-linux-amd64 /usr/local/bin/agentapi
if [ "${var.experiment_report_tasks}" = "true" ]; then
echo "Configuring Claude Code to report tasks via Coder MCP..."
coder exp mcp configure claude-code ${var.folder}
coder exp mcp configure claude-code ${var.folder} --ai-agentapi-url http://localhost:3284
fi
# Run post-install script if provided
if [ -n "${local.encoded_post_install_script}" ]; then
echo "Running post-install script..."
echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh
@@ -241,97 +149,62 @@ resource "coder_script" "claude_code" {
/tmp/post_install.sh
fi
# Handle terminal multiplexer selection (tmux or screen)
if [ "${var.experiment_use_tmux}" = "true" ] && [ "${var.experiment_use_screen}" = "true" ]; then
echo "Error: Both experiment_use_tmux and experiment_use_screen cannot be true simultaneously."
echo "Please set only one of them to true."
exit 1
fi
if [ "${var.experiment_tmux_session_persistence}" = "true" ] && [ "${var.experiment_use_tmux}" != "true" ]; then
echo "Error: Session persistence requires tmux to be enabled."
echo "Please set experiment_use_tmux = true when using session persistence."
exit 1
fi
# Run with tmux if enabled
if [ "${var.experiment_use_tmux}" = "true" ]; then
if ! command_exists tmux; then
install_tmux
fi
if [ "${var.experiment_tmux_session_persistence}" = "true" ]; then
echo "Setting up tmux session persistence..."
if ! command_exists git; then
echo "Git not found, installing git..."
if command_exists apt-get; then
sudo apt-get update && sudo apt-get install -y git
elif command_exists yum; then
sudo yum install -y git
elif command_exists dnf; then
sudo dnf install -y git
elif command_exists pacman; then
sudo pacman -S --noconfirm git
elif command_exists apk; then
sudo apk add git
else
echo "Error: Unable to install git automatically. Package manager not recognized."
echo "Please install git manually to enable session persistence."
exit 1
fi
fi
mkdir -p ~/.tmux/plugins
if [ ! -d ~/.tmux/plugins/tpm ]; then
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
fi
cat > ~/.tmux.conf << EOF
# Claude Code tmux persistence configuration
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
# Configure session persistence
set -g @resurrect-processes ':all:'
set -g @resurrect-capture-pane-contents 'on'
set -g @resurrect-save-bash-history 'on'
set -g @continuum-restore 'on'
set -g @continuum-save-interval '${var.experiment_tmux_session_save_interval}'
set -g @continuum-boot 'on'
set -g @continuum-save-on 'on'
# Initialize plugin manager
run '~/.tmux/plugins/tpm/tpm'
EOF
~/.tmux/plugins/tpm/scripts/install_plugins.sh
fi
echo "Running Claude Code in the background with tmux..."
# Check if tmux is installed
if ! command_exists tmux; then
echo "Error: tmux is not installed. Please install tmux manually."
exit 1
fi
touch "$HOME/.claude-code.log"
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
if [ "${var.experiment_tmux_session_persistence}" = "true" ]; then
sleep 3
if ! tmux has-session -t claude-code 2>/dev/null; then
# Only create a new session if one doesn't exist
tmux new-session -d -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""
fi
else
if ! tmux has-session -t claude-code 2>/dev/null; then
tmux new-session -d -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""
# use low width to fit in the tasks UI sidebar. height is adjusted to ~match the default 80k (80x1000) characters
# visible in the terminal screen.
tmux new-session -d -s claude-code-agentapi -c ${var.folder} 'agentapi server --term-width 67 --term-height 1190 -- bash -c "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""; exec bash'
echo "Waiting for agentapi server to start on port 3284..."
for i in $(seq 1 15); do
if lsof -i :3284 | grep -q 'LISTEN'; then
echo "agentapi server started on port 3284."
break
fi
echo "Waiting... ($i/15)"
sleep 1
done
if ! lsof -i :3284 | grep -q 'LISTEN'; then
echo "Error: agentapi server did not start on port 3284 after 15 seconds."
exit 1
fi
tmux new-session -d -s claude-code -c ${var.folder} "agentapi attach"
fi
# Run with screen if enabled
if [ "${var.experiment_use_screen}" = "true" ]; then
echo "Running Claude Code in the background..."
# Check if screen is installed
if ! command_exists screen; then
echo "Error: screen is not installed. Please install screen manually."
exit 1
fi
touch "$HOME/.claude-code.log"
# Ensure the screenrc exists
if [ ! -f "$HOME/.screenrc" ]; then
echo "Creating ~/.screenrc and adding multiuser settings..." | tee -a "$HOME/.claude-code.log"
echo -e "multiuser on\nacladd $(whoami)" > "$HOME/.screenrc"
@@ -346,7 +219,6 @@ EOF
echo "Adding 'acladd $(whoami)' to ~/.screenrc..." | tee -a "$HOME/.claude-code.log"
echo "acladd $(whoami)" >> "$HOME/.screenrc"
fi
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
@@ -356,6 +228,7 @@ EOF
exec bash
'
else
# Check if claude is installed before running
if ! command_exists claude; then
echo "Error: Claude Code is not installed. Please enable install_claude_code or install it manually."
exit 1
@@ -365,6 +238,20 @@ EOF
run_on_start = true
}
resource "coder_app" "claude_code_web" {
slug = "claude-code-web"
display_name = "Claude Code Web"
agent_id = var.agent_id
url = "http://localhost:3284/"
icon = var.icon
subdomain = true
healthcheck {
url = "http://localhost:3284/status"
interval = 5
threshold = 3
}
}
resource "coder_app" "claude_code" {
slug = "claude-code"
display_name = "Claude Code"
@@ -377,16 +264,20 @@ resource "coder_app" "claude_code" {
export LC_ALL=en_US.UTF-8
if [ "${var.experiment_use_tmux}" = "true" ]; then
if ! tmux has-session -t claude-code-agentapi 2>/dev/null; then
echo "Starting a new Claude Code agentapi tmux session." | tee -a "$HOME/.claude-code.log"
# use low width to fit in the tasks UI sidebar. height is adjusted to ~match the default 80k (80x1000) characters
# visible in the terminal screen.
tmux new-session -d -s claude-code-agentapi -c ${var.folder} 'agentapi server --term-width 67 --term-height 1190 -- bash -c "claude --dangerously-skip-permissions"; exec bash'
fi
if tmux has-session -t claude-code 2>/dev/null; then
echo "Attaching to existing Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
# If Claude isn't running in the session, start it without the prompt
if ! tmux list-panes -t claude-code -F '#{pane_current_command}' | grep -q "claude"; then
tmux send-keys -t claude-code "cd ${var.folder} && claude -c --dangerously-skip-permissions" C-m
fi
tmux attach-session -t claude-code
else
echo "Starting a new Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
tmux new-session -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions | tee -a \"$HOME/.claude-code.log\"; exec bash"
tmux new-session -s claude-code -c ${var.folder} "agentapi attach; exec bash"
fi
elif [ "${var.experiment_use_screen}" = "true" ]; then
if screen -list | grep -q "claude-code"; then
@@ -394,7 +285,7 @@ resource "coder_app" "claude_code" {
screen -xRR claude-code
else
echo "Starting a new Claude Code screen session." | tee -a "$HOME/.claude-code.log"
screen -S claude-code bash -c 'claude --dangerously-skip-permissions | tee -a "$HOME/.claude-code.log"; exec bash'
screen -S claude-code bash -c 'agentapi attach; exec bash'
fi
else
cd ${var.folder}
+21 -13
View File
@@ -66,6 +66,27 @@ data "coder_parameter" "ai_prompt" {
mutable = true
}
# Set the prompt and system prompt for Goose via environment variables
resource "coder_agent" "main" {
# ...
env = {
GOOSE_SYSTEM_PROMPT = <<-EOT
You are a helpful assistant that can help write code.
Run all long running tasks (e.g. npm run dev) in the background and not in the foreground.
Periodically check in on background tasks.
Notify Coder of the status of the task before and after your steps.
EOT
GOOSE_TASK_PROMPT = data.coder_parameter.ai_prompt.value
# An API key is required for experiment_auto_configure
# See https://block.github.io/goose/docs/getting-started/providers
ANTHROPIC_API_KEY = var.anthropic_api_key # or use a coder_parameter
}
}
module "goose" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/goose/coder"
@@ -75,19 +96,6 @@ module "goose" {
install_goose = true
goose_version = "v1.0.16"
# Set prompts and API key directly in the module
system_prompt = <<-EOT
You are a helpful assistant that can help write code.
Run all long running tasks (e.g. npm run dev) in the background and not in the foreground.
Periodically check in on background tasks.
Notify Coder of the status of the task before and after your steps.
EOT
task_prompt = data.coder_parameter.ai_prompt.value
anthropic_api_key = var.anthropic_api_key # or use a coder_parameter
# Enable experimental features
experiment_report_tasks = true
-41
View File
@@ -114,25 +114,6 @@ variable "experiment_additional_extensions" {
default = null
}
variable "system_prompt" {
type = string
description = "System prompt for Goose."
default = ""
}
variable "task_prompt" {
type = string
description = "Task prompt for Goose."
default = ""
}
variable "anthropic_api_key" {
type = string
description = "Anthropic API key for Goose."
default = ""
sensitive = true
}
locals {
base_extensions = <<-EOT
coder:
@@ -169,28 +150,6 @@ EOT
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
}
# Set environment variables for Goose
resource "coder_env" "goose_system_prompt" {
count = var.system_prompt != "" ? 1 : 0
agent_id = var.agent_id
name = "GOOSE_SYSTEM_PROMPT"
value = var.system_prompt
}
resource "coder_env" "goose_task_prompt" {
count = var.task_prompt != "" ? 1 : 0
agent_id = var.agent_id
name = "GOOSE_TASK_PROMPT"
value = var.task_prompt
}
resource "coder_env" "anthropic_api_key" {
count = var.anthropic_api_key != "" ? 1 : 0
agent_id = var.agent_id
name = "ANTHROPIC_API_KEY"
value = var.anthropic_api_key
}
# Install and Initialize Goose
resource "coder_script" "goose" {
agent_id = var.agent_id
@@ -1,74 +0,0 @@
---
display_name: Windows RDP Desktop
description: Enable RDP on Windows and add a one-click Coder Desktop button for seamless access
icon: ../../../../.icons/desktop.svg
maintainer_github: coder
verified: true
supported_os: [windows]
tags: [rdp, windows, desktop, remote]
---
# Windows RDP Desktop
This module enables Remote Desktop Protocol (RDP) on Windows workspaces and adds a one-click button to launch RDP sessions directly through [Coder Desktop](https://coder.com/docs/user-guides/desktop). It provides a complete, standalone solution for RDP access, eliminating the need for manual configuration or port forwarding through the Coder CLI.
> **Note**: [Coder Desktop](https://coder.com/docs/user-guides/desktop) is required on client devices to use the Local Windows RDP access feature.
```tf
module "rdp_desktop" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/local-windows-rdp/coder"
version = "1.0.0"
agent_id = coder_agent.main.id
agent_name = coder_agent.main.name
}
```
## Features
-**Standalone Solution**: Automatically configures RDP on Windows workspaces
-**One-click Access**: Launch RDP sessions directly through Coder Desktop
-**No Port Forwarding**: Uses Coder Desktop URI handling
-**Auto-configuration**: Sets up Windows firewall, services, and authentication
-**Secure**: Configurable credentials with sensitive variable handling
-**Customizable**: Display name, credentials, and UI ordering options
## What This Module Does
1. **Enables RDP** on the Windows workspace
2. **Sets the administrator password** for RDP authentication
3. **Configures Windows Firewall** to allow RDP connections
4. **Starts RDP services** automatically
5. **Creates a Coder Desktop button** for one-click access
## Examples
### Basic Usage
Uses default credentials (Username: `Administrator`, Password: `coderRDP!`):
```tf
module "rdp_desktop" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/local-windows-rdp/coder"
version = "1.0.0"
agent_id = coder_agent.main.id
agent_name = coder_agent.main.name
}
```
### Custom display name
Specify a custom display name for the `coder_app` button:
```tf
module "rdp_desktop" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/local-windows-rdp/coder"
version = "1.0.0"
agent_id = coder_agent.windows.id
agent_name = "windows"
display_name = "Windows Desktop"
order = 1
}
```
@@ -1,120 +0,0 @@
# PowerShell script to configure RDP for Coder Desktop access
# This script enables RDP, sets the admin password, and configures necessary settings
Write-Output "[Coder RDP Setup] Starting RDP configuration..."
# Function to set the administrator password
function Set-AdminPassword {
param (
[string]$adminUsername,
[string]$adminPassword
)
Write-Output "[Coder RDP Setup] Setting password for user: $adminUsername"
try {
# Convert password to secure string
$securePassword = ConvertTo-SecureString -AsPlainText $adminPassword -Force
# Set the password for the user
Get-LocalUser -Name $adminUsername | Set-LocalUser -Password $securePassword
# Enable the user account (in case it's disabled)
Get-LocalUser -Name $adminUsername | Enable-LocalUser
Write-Output "[Coder RDP Setup] Successfully set password for $adminUsername"
} catch {
Write-Error "[Coder RDP Setup] Failed to set password: $_"
exit 1
}
}
# Function to enable and configure RDP
function Enable-RDP {
Write-Output "[Coder RDP Setup] Enabling Remote Desktop..."
try {
# Enable RDP
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name "fDenyTSConnections" -Value 0 -Force
# Disable Network Level Authentication (NLA) for easier access
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name "UserAuthentication" -Value 0 -Force
# Set security layer to RDP Security Layer
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name "SecurityLayer" -Value 1 -Force
Write-Output "[Coder RDP Setup] RDP enabled successfully"
} catch {
Write-Error "[Coder RDP Setup] Failed to enable RDP: $_"
exit 1
}
}
# Function to configure Windows Firewall for RDP
function Configure-Firewall {
Write-Output "[Coder RDP Setup] Configuring Windows Firewall for RDP..."
try {
# Enable RDP firewall rules
Enable-NetFirewallRule -DisplayGroup "Remote Desktop" -ErrorAction SilentlyContinue
# If the above fails, try alternative method
if ($LASTEXITCODE -ne 0) {
netsh advfirewall firewall set rule group="remote desktop" new enable=Yes
}
Write-Output "[Coder RDP Setup] Firewall configured successfully"
} catch {
Write-Warning "[Coder RDP Setup] Failed to configure firewall rules: $_"
# Continue anyway as RDP might still work
}
}
# Function to ensure RDP service is running
function Start-RDPService {
Write-Output "[Coder RDP Setup] Starting Remote Desktop Services..."
try {
# Start the Terminal Services
Set-Service -Name "TermService" -StartupType Automatic -ErrorAction SilentlyContinue
Start-Service -Name "TermService" -ErrorAction SilentlyContinue
# Start Remote Desktop Services UserMode Port Redirector
Set-Service -Name "UmRdpService" -StartupType Automatic -ErrorAction SilentlyContinue
Start-Service -Name "UmRdpService" -ErrorAction SilentlyContinue
Write-Output "[Coder RDP Setup] RDP services started successfully"
} catch {
Write-Warning "[Coder RDP Setup] Some RDP services may not have started: $_"
# Continue anyway
}
}
# Main execution
try {
# Template variables from Terraform
$username = "${username}"
$password = "${password}"
# Validate inputs
if ([string]::IsNullOrWhiteSpace($username) -or [string]::IsNullOrWhiteSpace($password)) {
Write-Error "[Coder RDP Setup] Username or password is empty"
exit 1
}
# Execute configuration steps
Set-AdminPassword -adminUsername $username -adminPassword $password
Enable-RDP
Configure-Firewall
Start-RDPService
Write-Output "[Coder RDP Setup] RDP configuration completed successfully!"
Write-Output "[Coder RDP Setup] You can now connect using:"
Write-Output " Username: $username"
Write-Output " Password: [hidden]"
Write-Output " Port: 3389 (default)"
} catch {
Write-Error "[Coder RDP Setup] An unexpected error occurred: $_"
exit 1
}
@@ -1,184 +0,0 @@
import { describe, expect, it } from "bun:test";
import {
type TerraformState,
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "~test";
type TestVariables = Readonly<{
agent_id: string;
agent_name: string;
username?: string;
password?: string;
display_name?: string;
order?: number;
}>;
function findRdpApp(state: TerraformState) {
for (const resource of state.resources) {
const isRdpAppResource =
resource.type === "coder_app" && resource.name === "rdp_desktop";
if (!isRdpAppResource) {
continue;
}
for (const instance of resource.instances) {
if (instance.attributes.slug === "rdp-desktop") {
return instance.attributes;
}
}
}
return null;
}
function findRdpScript(state: TerraformState) {
for (const resource of state.resources) {
const isRdpScriptResource =
resource.type === "coder_script" && resource.name === "rdp_setup";
if (!isRdpScriptResource) {
continue;
}
for (const instance of resource.instances) {
if (instance.attributes.display_name === "Configure RDP") {
return instance.attributes;
}
}
}
return null;
}
describe("local-windows-rdp", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "test-agent",
});
it("should create RDP app with default values", async () => {
const state = await runTerraformApply<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "main",
});
const app = findRdpApp(state);
// Verify the app was created
expect(app).not.toBeNull();
expect(app?.slug).toBe("rdp-desktop");
expect(app?.display_name).toBe("RDP Desktop");
expect(app?.icon).toBe("/icon/desktop.svg");
expect(app?.external).toBe(true);
// Verify the URI format
expect(app?.url).toStartWith("coder://");
expect(app?.url).toContain("/v0/open/ws/");
expect(app?.url).toContain("/agent/main/rdp");
expect(app?.url).toContain("username=Administrator");
expect(app?.url).toContain("password=coderRDP!");
});
it("should create RDP configuration script", async () => {
const state = await runTerraformApply<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "main",
});
const script = findRdpScript(state);
// Verify the script was created
expect(script).not.toBeNull();
expect(script?.display_name).toBe("Configure RDP");
expect(script?.icon).toBe("/icon/desktop.svg");
expect(script?.run_on_start).toBe(true);
expect(script?.run_on_stop).toBe(false);
// Verify the script contains PowerShell configuration
expect(script?.script).toContain("Set-AdminPassword");
expect(script?.script).toContain("Enable-RDP");
expect(script?.script).toContain("Configure-Firewall");
expect(script?.script).toContain("Start-RDPService");
});
it("should create RDP app with custom values", async () => {
const state = await runTerraformApply<TestVariables>(import.meta.dir, {
agent_id: "custom-agent-id",
agent_name: "windows-agent",
username: "CustomUser",
password: "CustomPass123!",
display_name: "Custom RDP",
order: 5,
});
const app = findRdpApp(state);
// Verify custom values
expect(app?.display_name).toBe("Custom RDP");
expect(app?.order).toBe(5);
// Verify custom credentials in URI
expect(app?.url).toContain("/agent/windows-agent/rdp");
expect(app?.url).toContain("username=CustomUser");
expect(app?.url).toContain("password=CustomPass123!");
});
it("should pass custom credentials to PowerShell script", async () => {
const state = await runTerraformApply<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "main",
username: "TestAdmin",
password: "TestPassword123!",
});
const script = findRdpScript(state);
// Verify custom credentials are in the script
expect(script?.script).toContain('$username = "TestAdmin"');
expect(script?.script).toContain('$password = "TestPassword123!"');
});
it("should handle sensitive password variable", async () => {
const state = await runTerraformApply<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "main",
password: "SensitivePass123!",
});
const app = findRdpApp(state);
// Verify password is included in URI even when sensitive
expect(app?.url).toContain("password=SensitivePass123!");
});
it("should use correct default agent name", async () => {
const state = await runTerraformApply<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "main",
});
const app = findRdpApp(state);
expect(app?.url).toContain("/agent/main/rdp");
});
it("should construct proper Coder URI format", async () => {
const state = await runTerraformApply<TestVariables>(import.meta.dir, {
agent_id: "test-agent-id",
agent_name: "test-agent",
username: "TestUser",
password: "TestPass",
});
const app = findRdpApp(state);
// Verify complete URI structure
expect(app?.url).toMatch(
/^coder:\/\/[^\/]+\/v0\/open\/ws\/[^\/]+\/agent\/test-agent\/rdp\?username=TestUser&password=TestPass$/,
);
});
});
@@ -1,81 +0,0 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 2.5"
}
}
}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "agent_name" {
type = string
description = "The name of the Coder agent."
}
variable "username" {
type = string
description = "The username for RDP authentication."
default = "Administrator"
}
variable "password" {
type = string
description = "The password for RDP authentication."
default = "coderRDP!"
sensitive = true
}
variable "display_name" {
type = string
description = "The display name for the RDP app button."
default = "RDP Desktop"
}
variable "order" {
type = number
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
default = null
}
variable "group" {
type = string
description = "The name of a group that this app belongs to."
default = null
}
locals {
# Extract server name from workspace access URL
server_name = regex("https?:\\/\\/([^\\/]+)", data.coder_workspace.me.access_url)[0]
}
data "coder_workspace" "me" {}
resource "coder_script" "rdp_setup" {
agent_id = var.agent_id
display_name = "Configure RDP"
icon = "/icon/desktop.svg"
script = templatefile("${path.module}/configure-rdp.ps1", {
username = var.username
password = var.password
})
run_on_start = true
}
resource "coder_app" "rdp_desktop" {
agent_id = var.agent_id
slug = "rdp-desktop"
display_name = var.display_name
url = "coder://${local.server_name}/v0/open/ws/${data.coder_workspace.me.name}/agent/${var.agent_name}/rdp?username=${var.username}&password=${var.password}"
icon = "/icon/desktop.svg"
external = true
order = var.order
group = var.group
}
+5 -5
View File
@@ -15,7 +15,7 @@ Automatically install [Visual Studio Code Server](https://code.visualstudio.com/
module "vscode-web" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/vscode-web/coder"
version = "1.3.0"
version = "1.2.0"
agent_id = coder_agent.example.id
accept_license = true
}
@@ -31,7 +31,7 @@ module "vscode-web" {
module "vscode-web" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/vscode-web/coder"
version = "1.3.0"
version = "1.2.0"
agent_id = coder_agent.example.id
install_prefix = "/home/coder/.vscode-web"
folder = "/home/coder"
@@ -45,7 +45,7 @@ module "vscode-web" {
module "vscode-web" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/vscode-web/coder"
version = "1.3.0"
version = "1.2.0"
agent_id = coder_agent.example.id
extensions = ["github.copilot", "ms-python.python", "ms-toolsai.jupyter"]
accept_license = true
@@ -60,7 +60,7 @@ Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarte
module "vscode-web" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/vscode-web/coder"
version = "1.3.0"
version = "1.2.0"
agent_id = coder_agent.example.id
extensions = ["dracula-theme.theme-dracula"]
settings = {
@@ -78,7 +78,7 @@ By default, this module installs the latest. To pin a specific version, retrieve
module "vscode-web" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/vscode-web/coder"
version = "1.3.0"
version = "1.2.0"
agent_id = coder_agent.example.id
commit_id = "e54c774e0add60467559eb0d1e229c6452cf8447"
accept_license = true
@@ -121,12 +121,6 @@ variable "use_cached" {
default = false
}
variable "disable_trust" {
type = bool
description = "Disables workspace trust protection for VS Code Web."
default = false
}
variable "extensions_dir" {
type = string
description = "Override the directory to store extensions in."
@@ -175,7 +169,6 @@ resource "coder_script" "vscode-web" {
SETTINGS : replace(jsonencode(var.settings), "\"", "\\\""),
OFFLINE : var.offline,
USE_CACHED : var.use_cached,
DISABLE_TRUST : var.disable_trust,
EXTENSIONS_DIR : var.extensions_dir,
FOLDER : var.folder,
AUTO_INSTALL_EXTENSIONS : var.auto_install_extensions,
+2 -8
View File
@@ -16,16 +16,10 @@ if [ -n "${SERVER_BASE_PATH}" ]; then
SERVER_BASE_PATH_ARG="--server-base-path=${SERVER_BASE_PATH}"
fi
# Set disable workspace trust
DISABLE_TRUST_ARG=""
if [ "${DISABLE_TRUST}" = true ]; then
DISABLE_TRUST_ARG="--disable-workspace-trust"
fi
run_vscode_web() {
echo "👷 Running $VSCODE_WEB serve-local $EXTENSION_ARG $SERVER_BASE_PATH_ARG $DISABLE_TRUST_ARG --port ${PORT} --host 127.0.0.1 --accept-server-license-terms --without-connection-token --telemetry-level ${TELEMETRY_LEVEL} in the background..."
echo "👷 Running $VSCODE_WEB serve-local $EXTENSION_ARG $SERVER_BASE_PATH_ARG --port ${PORT} --host 127.0.0.1 --accept-server-license-terms --without-connection-token --telemetry-level ${TELEMETRY_LEVEL} in the background..."
echo "Check logs at ${LOG_PATH}!"
"$VSCODE_WEB" serve-local "$EXTENSION_ARG" "$SERVER_BASE_PATH_ARG" "$DISABLE_TRUST_ARG" --port "${PORT}" --host 127.0.0.1 --accept-server-license-terms --without-connection-token --telemetry-level "${TELEMETRY_LEVEL}" > "${LOG_PATH}" 2>&1 &
"$VSCODE_WEB" serve-local "$EXTENSION_ARG" "$SERVER_BASE_PATH_ARG" --port "${PORT}" --host 127.0.0.1 --accept-server-license-terms --without-connection-token --telemetry-level "${TELEMETRY_LEVEL}" > "${LOG_PATH}" 2>&1 &
}
# Check if the settings file exists...