အရင်ဆုံး boundary ကို သတ်မှတ်ပါ
Pi က built-in permission system မပါဘဲ၊ သူ run နေတဲ့ Linux user ရဲ့ permission အတိုင်း file နဲ့ command တွေကို ကိုင်နိုင်တယ်။ ဒီအဆင့်မှာ Cloud Shell ထဲက သီးသန့် test directory တစ်ခုအတွင်းမှာပဲ စမ်းပါ။
Bot မှားပြောတာ၊ model မှားနားလည်တာကြောင့် ဖိုင်တွေ ထိနိုင်တယ်။ Test directory သုံးပြီး workflow ကို နားလည်သွားမှ access တိုးပါ။
Cloud Shell ထဲမှာ Pi ကို တင်ပါ
Official installer က Linux မှာ Pi နဲ့ လိုအပ်တဲ့ Node.js version ကို စစ်ပြီး တင်ပေးနိုင်တယ်။ Install ပြီးရင် terminal အသစ်ဖွင့် သို့မဟုတ် shell profile ကို reload လုပ်ပါ။
curl -fsSL https://pi.dev/install.sh | sh
source ~/.bashrc 2>/dev/null || true
pi --version
pi --help | head -30
pi --version မှာ version ပေါ်ရမယ်။ command not found ဆို terminal tab အသစ်တစ်ခု ဖွင့်ပြီး ထပ်စစ်ပါ။
RelayModels ကို custom provider အဖြစ် ချိတ်ပါ
Pi က OpenAI-compatible endpoint ကို ~/.pi/agent/models.json မှာ ထည့်လို့ရတယ်။ Key ကို JSON ထဲ တိုက်ရိုက်မရေးဘဲ environment variable သုံးထားမယ်။
Provider config ဖန်တီးရန်
mkdir -p ~/.pi/agent
chmod 700 ~/.pi ~/.pi/agent 2>/dev/null || true
cat > ~/.pi/agent/models.json <<'JSON'
{
"providers": {
"relaymodels": {
"baseUrl": "https://api.relaymodels.com/v1",
"api": "openai-completions",
"apiKey": "$RELAYMODELS_API_KEY",
"models": [
{ "id": "claude-sonnet-5", "name": "Claude Sonnet 5 — RelayModels" }
]
}
}
}
JSON
chmod 600 ~/.pi/agent/models.json
Key ကို shell ထဲ ခဏထည့်ပြီး test လုပ်ရန်
read -rsp "RelayModels API key: " RELAYMODELS_API_KEY; echo
export RELAYMODELS_API_KEY
pi --provider relaymodels --model claude-sonnet-5 \
--no-session -p "Reply exactly: PI_READY"
unset RELAYMODELS_API_KEY
PI_READY ပြန်လာရင် model ချိတ်ပြီးပါပြီ။ Key ကို command-line argument မပေးထားတာကြောင့် shell history ထဲ key မကျန်ဘူး။
Private bot token နဲ့ owner ID ယူပါ
- Telegram မှာ official @BotFather ကိုဖွင့်ပါ။
- /newbot ပို့ပြီး bot name နဲ့ bot နဲ့ဆုံးတဲ့ username ရွေးပါ။
- BotFather ပေးတဲ့ token ကို မည်သူ့ကိုမှ မပို့ပါနဲ့။ မင်း bot အသစ်ကိုဖွင့်ပြီး /start တစ်ခါပို့ပါ။
မင်းရဲ့ numeric Telegram ID ကို ရယူရန်
Bot ကို message ပို့ပြီးမှ အောက် command ကို Cloud Shell မှာ run ပါ။ Token ကို hidden input နဲ့ပဲ ယူမယ်။
read -rsp "Telegram bot token: " BOT_TOKEN; echo
curl -fsS "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates" \
| python3 -c 'import sys,json; d=json.load(sys.stdin); print(*sorted({str(x["message"]["from"]["id"]) for x in d.get("result",[]) if "message" in x}), sep="\n")'
Output ထဲက မင်း ID ကို အပေါ်က Telegram numeric ID field မှာထည့်ပါ။ ဘာမှမပေါ်ရင် bot ကို message အသစ်တစ်ခါပို့ပြီး command ပြန်runပါ။
Owner-only Telegram → Pi bridge ကို တင်ပါ
ဒီ bridge က long polling သုံးလို့ public port၊ domain၊ webhook မလိုဘူး။ Normal message တွေကို plan-only အဖြစ်ပို့ပြီး /run နဲ့စတဲ့ message သာ Cloud Shell ထဲက test directory မှာ Pi ကို tool သုံးခွင့်ပေးမယ်။
A. Folder နဲ့ package ဖန်တီးရန်
mkdir -p ~/pi-telegram-bridge ~/pi-remote-agent
cd ~/pi-telegram-bridge
npm init -y
npm install --ignore-scripts node-telegram-bot-api
B. Pi ကို isolated Docker sandbox ထဲတွင် ပြင်ဆင်ရန်
Telegram ကနေခေါ်တဲ့ Pi ကို Cloud Shell account နဲ့ secret ဖိုင်တွေ မမြင်နိုင်အောင် container ထဲမှာ သီးခြား run မယ်။ Container က test workspace နဲ့ model config ဖိုင်ကိုပဲ ရမယ်။
cat > ~/pi-telegram-bridge/Dockerfile.pi <<'DOCKER'
FROM node:24-bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends bash ca-certificates git ripgrep \
&& npm install -g --ignore-scripts @earendil-works/pi-coding-agent \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
ENTRYPOINT ["pi"]
DOCKER
cd ~/pi-telegram-bridge
docker build -t pi-telegram-sandbox -f Dockerfile.pi .
C. Pi အတွက် safety rules ထည့်ရန်
cat > ~/pi-remote-agent/AGENTS.md <<'MD'
# Cloud Shell test workspace rules
- Stay inside this test workspace. Do not access remote servers or accounts.
- Never read, print, copy, or modify private keys, tokens, `.env` files, or credentials.
- Before any change: inspect current state, explain the smallest safe action, then act.
- Never delete user data or files outside this workspace.
- Never install system-wide software, change authentication, or alter Cloud Shell settings.
- Prefer backups, reversible edits, and syntax checks.
- Report every command run, changed file, verification result, and rollback path.
MD
D. Bridge code ဖန်တီးရန်
cat > ~/pi-telegram-bridge/bot.cjs <<'JS'
'use strict';
const TelegramBot = require('node-telegram-bot-api');
const { spawn } = require('node:child_process');
const required = ['BOT_TOKEN', 'TELEGRAM_USER_ID', 'RELAYMODELS_API_KEY'];
for (const name of required) {
if (!process.env[name]) throw new Error(`Missing ${name} in .env`);
}
const bot = new TelegramBot(process.env.BOT_TOKEN, { polling: true });
const ownerId = String(process.env.TELEGRAM_USER_ID);
const workDir = process.env.WORK_DIR || `${process.env.HOME}/pi-remote-agent`;
const modelConfig = `${process.env.HOME}/.pi/agent/models.json`;
const piImage = process.env.PI_IMAGE || 'pi-telegram-sandbox';
const provider = process.env.PI_PROVIDER || 'relaymodels';
const model = process.env.PI_MODEL || 'claude-sonnet-5';
let busy = false;
function splitText(text, max = 3800) {
const parts = [];
let rest = text || '(Pi returned no text)';
while (rest.length > max) {
let cut = rest.lastIndexOf('\n', max);
if (cut < max * 0.5) cut = max;
parts.push(rest.slice(0, cut));
rest = rest.slice(cut).replace(/^\n/, '');
}
parts.push(rest);
return parts;
}
function run(cmd, args, options = {}) {
return new Promise((resolve) => {
const child = spawn(cmd, args, {
cwd: options.cwd || workDir,
env: options.env || process.env,
stdio: ['ignore', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
const cap = 120000;
child.stdout.on('data', d => { stdout = (stdout + d).slice(-cap); });
child.stderr.on('data', d => { stderr = (stderr + d).slice(-20000); });
const timer = setTimeout(() => child.kill('SIGTERM'), options.timeout || 600000);
child.on('error', err => {
clearTimeout(timer);
resolve({ code: -1, stdout, stderr: err.message });
});
child.on('close', code => {
clearTimeout(timer);
resolve({ code, stdout: stdout.trim(), stderr: stderr.trim() });
});
});
}
async function sendLong(chatId, text) {
for (const part of splitText(text)) await bot.sendMessage(chatId, part);
}
bot.on('message', async (msg) => {
const chatId = msg.chat.id;
if (String(msg.from?.id) !== ownerId) return;
const text = (msg.text || '').trim();
if (!text) return bot.sendMessage(chatId, 'Text message ပဲ အရင်စမ်းပါ။');
if (text === '/start' || text === '/help') {
return bot.sendMessage(chatId,
'Normal message = plan / answer only\n' +
'/status = Cloud Shell read-only health check\n' +
'/run <task> = test workspace ထဲမှာ Pi ကို လုပ်ခိုင်းမယ်\n\n' +
'Sensitive ဖိုင်တွေ၊ remote server တွေကို မထိပါနဲ့။'
);
}
if (text === '/status') {
const r = await run('bash', ['-lc',
'hostname; uptime; printf "\\nDISK\\n"; df -h "$HOME"; printf "\\nMEMORY\\n"; free -h'
], { timeout: 30000 });
return sendLong(chatId, r.code === 0 ? r.stdout : `Status error (${r.code})\n${r.stderr}`);
}
if (busy) return bot.sendMessage(chatId, 'Pi က အလုပ်လုပ်နေတယ်။ ပြီးမှ နောက်တစ်ခု ပို့ပါ။');
busy = true;
await bot.sendChatAction(chatId, 'typing');
const execute = text.startsWith('/run ');
const task = execute ? text.slice(5).trim() : text;
const prefix = execute
? 'EXECUTION MODE. Work only inside the current Cloud Shell test workspace. Follow AGENTS.md. ' +
'Do not access remote servers. Inspect first, make the smallest reversible change, verify it, and report exact results. '
: 'PLAN-ONLY MODE. Answer or create a plan. Do not change files, do not run shell commands, ' +
'and do not claim that any action was completed. ';
const piArgs = ['--provider', provider, '--model', model, '--no-session'];
if (!execute) piArgs.push('--tools', 'read,grep,find,ls');
piArgs.push('-p', `${prefix}\n\nUser request:\n${task}`);
const dockerArgs = [
'run', '--rm', '--read-only', '--network', 'bridge',
'--cap-drop=ALL', '--security-opt', 'no-new-privileges',
'--pids-limit', '256', '--memory', '1g', '--cpus', '1',
'--tmpfs', '/tmp:rw,noexec,nosuid,size=64m',
'-e', 'RELAYMODELS_API_KEY',
'-v', `${workDir}:/workspace:rw`,
'-v', `${modelConfig}:/root/.pi/agent/models.json:ro`,
'-w', '/workspace', piImage,
...piArgs
];
const safeEnv = {
PATH: process.env.PATH,
HOME: process.env.HOME,
RELAYMODELS_API_KEY: process.env.RELAYMODELS_API_KEY
};
const result = await run('docker', dockerArgs, { timeout: 600000, env: safeEnv });
busy = false;
if (result.code === 0) return sendLong(chatId, result.stdout);
return sendLong(chatId, `Pi error (${result.code})\n${result.stderr || result.stdout}`);
});
bot.on('polling_error', err => console.error('polling_error:', err.message));
console.log('Pi Telegram bridge is running for owner', ownerId);
JS
E. Secrets ကို permission 600 ဖြင့် သိမ်းရန်
အောက် command ကို run လုပ်တဲ့အခါ token နဲ့ key က screen ပေါ်မပေါ်ဘူး။ Telegram ID ကို အပေါ်က field ကနေ အလိုအလျောက်ထည့်ပေးထားတယ်။
cd ~/pi-telegram-bridge
umask 077
read -rsp "Telegram bot token: " BOT_TOKEN; echo
read -rsp "RelayModels API key: " RELAYMODELS_API_KEY; echo
cat > .env <<EOF
BOT_TOKEN=$BOT_TOKEN
TELEGRAM_USER_ID=YOUR_TELEGRAM_ID
RELAYMODELS_API_KEY=$RELAYMODELS_API_KEY
PI_PROVIDER=relaymodels
PI_MODEL=claude-sonnet-5
WORK_DIR=$HOME/pi-remote-agent
EOF
chmod 600 .env
unset BOT_TOKEN RELAYMODELS_API_KEY
ls -l .env
Bot ကို run ပြီး အဆင့်လိုက်စမ်းပါ
Bridge ကို Cloud Shell terminal ရဲ့ foreground မှာ run ပြီး Telegram ကနေ အဆင့်လိုက် စမ်းပါ။
cd ~/pi-telegram-bridge
node --env-file=.env bot.cjs
Pi Telegram bridge is running ပေါ်ရင် Telegram ကနေ ဒီ order အတိုင်း စမ်းပါ။
/helpBot command နဲ့ mode အဓိပ္ပာယ်တွေ ပြမယ်။
/statusLLM မသုံးဘဲ Cloud Shell hostname, uptime, disk, memory ကို read-only စစ်မယ်။
ဒီ task အတွက် plan တစ်ခုရေးNormal message က plan-only ဖြစ်ပြီး command မလုပ်ဘူး။
/run sample.txt ဖိုင်တစ်ခု ဖန်တီးExecution mode ဝင်ပြီး test workspace ထဲမှာ Pi ကို စမ်းမယ်။
/run sample.txt ထဲ PI_READY လို့ရေးပြီး ပြန်ဖတ်ပြ။ တခြားဖိုင်မပြင်နဲ့ လို့ ပို့ပါ။ ပြန်လာတဲ့ command နဲ့ output ကို Cloud Shell terminal မှာ cat ~/pi-remote-agent/sample.txt နဲ့ တိုက်စစ်ပါ။
Terminal ပိတ်မိသေးပေမယ့် ခဏဆက် run ချင်ရင်
tmux new -s pi-bot
cd ~/pi-telegram-bridge
node --env-file=.env bot.cjs
# Detach: Ctrl+B, ပြီးရင် D
# ပြန်ဝင်ရန်:
tmux attach -t pi-bot
Cloud Shell VM ကို Google ကပိတ်လိုက်ရင် tmux နဲ့ bot နှစ်ခုလုံးပိတ်မယ်။ ဒီအဆင့်က setup စမ်းဖို့ပဲ။