فی صارف OAuth رسائی کے ساتھ AI ایجنٹ کیسے بنایا جائے۔ [Full Handbook]

اگر آپ کا AI ایجنٹ ایک سے زیادہ افراد کی خدمت کرتا ہے، تو ٹول پر آنے والی ہر کال کا جواب کچھ اس طرح ہونا چاہیے: ایجنٹ کس کے لیے کام کرتا ہے؟ آئیے ایک AI ایجنٹ بنا کر اس کو حل کرنے کا طریقہ معلوم کرتے ہیں جو Slack اور GitHub سے جڑتا ہے۔

سلیک ریڈز اس صارف کی ورک اسپیس کو استعمال کرتی ہے۔ ایک GitHub مسئلہ اس صارف کے لیے قابل رسائی ذخیرہ میں بنایا گیا ہے۔ ایجنٹ غلط کال کر سکتے ہیں، لیکن انہیں غلط صارف کے رسائی کے حقوق کے ساتھ کام نہیں کرنا چاہیے۔

فکس دو حصوں پر مشتمل ہے، جو دونوں اس ٹیوٹوریل کے پہلے نصف میں نظر آتے ہیں۔

  1. ہر صارف انفرادی طور پر رسائی دیتا ہے۔ ایلس نے اپنے لیے سلیک کی منظوری دی۔ باب خود اس کی منظوری دیتا ہے۔

  2. ایجنٹ ایک شناخت کنندہ پاس کرتا ہے، ٹوکن نہیں۔ اس طرح کی تار: alice@example.com منتخب کریں کہ آپ کس کی گرانٹ کی رقم استعمال کرنا چاہتے ہیں۔ ایک فنکشن اسے کال کے وقت ٹوکن میں تبدیل کرتا ہے، اور یہ ٹوکن کبھی بھی ماڈل ان پٹ، ٹول اسکیما یا لاگز تک نہیں پہنچتا ہے۔

زیادہ تر ایجنٹ سبق دو پوائنٹس سے پہلے رک جاتے ہیں۔ صرف API کلید میں ہاتھ ڈالیں، ایک فنکشن پلگ ان کریں، اور ماڈل اسے کال کرے گا۔ ڈیزائن اس وقت تک کام کرتا ہے جب تک کہ دوسرا شخص ظاہر نہ ہو۔

پیٹرن کو کنکریٹ بنانے کے لیے، ہم ایک کمانڈ لائن ایجنٹ بناتے ہیں جو سلیک چینل دیکھتا ہے، اپنے لیے ایک پیغام کا فیصلہ کرتا ہے جو اصل کارروائی کو بیان کرتا ہے، اس کے لیے ایک GitHub ایشو جمع کرتا ہے، اور ایشو لنک کا استعمال کرتے ہوئے سلیک تھریڈ کا جواب دیتا ہے۔ تمام کالیں ایک صارف کی اپنی OAuth اجازتوں سے کی جاتی ہیں۔

OAuth بہاؤ (رضامندی ری ڈائریکٹ، state تصدیق، ٹوکن ایکسچینج، انکرپٹڈ اسٹوریج، اور ریفریش پاتھ چیک کریں۔ اس میں سے کوئی بھی لمبا نہیں ہے اور اسے مکمل طور پر دیکھنا وہی ہے جو آپ کو ان دعووں کی بجائے شناخت کے دعووں کی تصدیق کرنے کی اجازت دیتا ہے جو آپ یقین کرتے ہیں۔

دونوں موضوعات یہاں دائرہ کار سے باہر ہیں۔ یہ ماڈل سیاق و سباق پروٹوکول سرورز یا آواز یا حقیقی وقت کے میزبانوں کا احاطہ نہیں کرتا ہے۔ شناخت کے نمونے دونوں سیٹنگز پر لاگو ہوتے ہیں، لیکن پیری میٹر پلمبنگ اپنے طور پر قیمتی ہے۔

انڈیکس

کیا تعمیر کرنا ہے

ایک نمائندہ بلایا جاتا ہے۔ channel-watcher-agent. ہر رن چار کام انجام دیتا ہے:

  1. سلیک چینل میں حالیہ پیغامات پڑھیں۔

  2. ہر پیغام کے لیے، ہم ماڈل سے پوچھتے ہیں کہ آیا متن کسی بگ کی وضاحت کرتا ہے یا ایک مخصوص ایکشن آئٹم۔

  3. اہل پیغامات کے لیے ایک GitHub مسئلہ جمع کروائیں۔

  4. نئے شمارے کے لنک کے ساتھ اصل سلیک تھریڈ کا جواب دیں۔

شروع کرنے کے لیے کوئی بھی بٹن پر کلک نہیں کرتا ہے۔ سلیک پہلے سے ہی ایک اور پروڈکٹ پیش کرتا ہے، "اس پیغام سے مسئلہ بنائیں” ایکشن۔ یہاں، ایجنٹ چینل کو پڑھتا ہے، اپنا فیصلہ خود کرتا ہے، اور صرف وہی کارروائی کرتا ہے جسے وہ کارروائی کے لائق سمجھتا ہے۔

اسٹیک کو جان بوجھ کر چھوٹا رکھا گیا ہے۔

ٹکڑا کردار
Node.js، عام ES ماڈیول کوئی ویب فریم ورک نہیں، کوئی قطار نہیں۔
node:http OAuth کال بیک سرور
node:crypto ٹوکن خفیہ کاری
node:sqlite ٹوکن اسٹور جس میں انسٹال کرنے کے لیے کوئی انحصار نہیں ہے۔
Vercel AI SDK ماڈل کالز اور ٹول لوپس

5 میں سے 3 Node.js کے ساتھ آتے ہیں۔ صرف پیکیجز جو آپ انسٹال کرتے ہیں وہ ہیں AI SDK اور دوست۔

آخر میں آپ کے پاس ہوگا:

  • Slack اور GitHub دو OAuth ایپس ہیں جن پر صارفین ایک ساتھ رضامندی دیتے ہیں۔

  • صارفین اور فراہم کنندگان کے ذریعے داخل کردہ خفیہ کردہ ٹوکنز کا ذخیرہ۔

  • ایک ایجنٹ جو موجودہ صارف کی شناخت کنندہ کے ذریعے تصدیق کرتا ہے اور ٹوکنز کو ماڈل تک پہنچنے کی اجازت نہیں دیتا۔

  • ایک ٹول لوپ جو اس بات کا تعین کرتا ہے کہ آیا ماڈل کو کوئی مسئلہ پیش کرنا چاہیے یا نہیں۔

  • یہاں ایک ڈیمو ہے جہاں پہلے صارف کا ڈیٹا پڑھنے کے بجائے دوسرے صارف کا عمل رک جاتا ہے۔

مکمل شدہ کوڈ github.com/saif-shines/channel-watcher-agent پر ہے۔

شرطیں

اکاؤنٹس اور ٹولز:

  • Node.js 22.13 یا اس سے زیادہاور این پی ایم۔ ٹوکن اسٹوریج استعمال کرتا ہے: node:sqliteیہ اس ورژن سے مستحکم ہے۔

  • سست کام کی جگہ ایسی جگہیں ہیں جہاں آپ ایپس اور چینلز انسٹال کر سکتے ہیں جنہیں آپ دیکھ سکتے ہیں۔ ڈسپوزایبل چینلز بہترین کام کرتے ہیں۔

  • GitHub اکاؤنٹ یہ ایک ذخیرہ ہے جہاں ٹیسٹ کے مسائل کو جذب کیا جا سکتا ہے۔

  • ماڈل فراہم کنندہ کے لیے API کلید AI SDK سپورٹ کرتا ہے۔ مثال میں Anthropic استعمال کیا جاتا ہے۔

  • mkcertمقامی HTTPS سرٹیفکیٹ جاری کریں۔ وضاحت کرتا ہے کہ Slack اور GitHub OAuth ایپس کو رجسٹر کرنے کا یہ ایک عام طریقہ کیوں ہے۔ http://localhost کوئی کال بیک نہیں کیا جاتا ہے۔

کچھ مفید پس منظر کا علم ہے، لیکن اس میں سے کوئی بھی مشکل ضرورت نہیں ہے۔

  • async اور awaitایک چھوٹا نوڈ اسکرپٹ پڑھیں۔

  • اعلی سطحی OAuth 2.0: آپ کی ایپ صارف کو فراہم کنندہ کی طرف بھیجتی ہے، اور جب صارف رضامندی دیتا ہے، ایپ کو ایک ٹوکن موصول ہوتا ہے۔

  • ٹول کالز، بعض اوقات فنکشن کالز بھی کہلاتی ہیں۔ مندرجہ ذیل حصے اس بات کا احاطہ کرتے ہیں کہ آپ کو ٹیوٹوریل کے لیے کیا درکار ہوگا۔

شروع کرنے سے پہلے ایک انتباہ: ایجنٹ حقیقی نظام کو لکھتے ہیں۔ ایک حقیقی GitHub مسئلہ کھولیں اور ایک حقیقی سلیک پیغام پوسٹ کریں۔ یہ یقینی بنانے کے لیے کہ وہ صرف آپ کے مطلوبہ پیغامات کے لیے کام کرتے ہیں، ٹیسٹ سلیک چینلز اور ڈسپوزایبل GitHub ریپوزٹریز کا استعمال کریں۔

ایک ٹول ایک فنکشن ہے جو اپنے ان پٹ کے ساتھ ایک ماڈل کو پاس کرتا ہے۔ ماڈل خود فنکشن کو انجام نہیں دے سکتا۔ یہ صرف پوچھنے کی بات ہے: فون fileGithubIssue اس عنوان اور اس جسم کے ساتھ۔ جب آپ کا کوڈ کال کرتا ہے اور نتیجہ واپس کرتا ہے، تو ماڈل اس نتیجے کو اگلا مرحلہ منتخب کرنے کے لیے استعمال کرتا ہے۔

درخواست کرنا، عمل کرنا، واپس کرنا۔ ایکسچینج ایک مکمل طریقہ کار ہے اور "ایجنٹ” کہلانے والی ہر چیز اس کے گرد گھومتی ہے۔

ٹولز اور APIs کے درمیان فرق

ٹولز اور APIs ایک ہی کال کو لپیٹتے ہیں لیکن مختلف قارئین کے لیے لکھے جاتے ہیں۔

API آپ کے لیے لکھا گیا ہے۔ اس سے اندازہ ہوتا ہے کہ آپ نے مضمون پڑھا اور جانتے ہیں۔ thread_ts یہ فیلڈ سلیک پیغامات کو تھریڈ جوابات میں تبدیل کرتی ہے۔

ایک ماڈل کے لئے ایک ٹول لکھا گیا ہے جو کچھ نہیں پڑھتا ہے۔ لہذا، آلہ خود وضاحتی ہے.

  • کوئی راستہ نہیں نام ماڈل کا اندازہ اس طرح لگایا جا سکتا ہے: fileGithubIssue.

  • کوئی راستہ نہیں وضاحت یہ سادہ زبان میں ٹول کی وضاحت کرتا ہے، بشمول اسے کب استعمال نہ کیا جائے۔

  • کوئی راستہ نہیں خاکہ اس کے آدانوں کے لئے، ماڈل جانتا ہے: title یہ ایک مطلوبہ تار ہے۔

ذیل میں پروجیکٹ کے ٹولز میں سے ایک ہے۔ زیادہ تر کوڈ منطقی کے بجائے وضاحتی ہے۔

const fileGithubIssue = tool({
  description: 'File a GitHub issue for an actionable Slack message',
  inputSchema: z.object({
    title: z.string(),
    body: z.string(),
  }),
  execute: async ({ title, body }) => {
    // ... the actual API call goes here
  },
});

کہ description اور inputSchema یہ وہی ہے جو ماڈل دیکھتا ہے. کہ execute خصوصیات صرف آپ کی ہیں۔ شناخت اندر ہے۔ executeلہذا ماڈل کبھی نہیں جان سکے گا کہ کال کس اکاؤنٹ کے لئے جاری کی گئی تھی۔

ماڈلز ٹولز کو خام API کالوں سے بہتر کیوں ہینڈل کرتے ہیں۔

ان پٹ میں curl کمانڈ چسپاں کرنا اور ماڈل سے خالی جگہوں کو پُر کرنے کے لیے کہنا ممکن ہے۔ تاہم، یہ نقطہ نظر پیشین گوئی کے طریقوں میں ناکام ہوتا ہے.

ٹولز تین وجوہات کی بنا پر بہتر کام کرتے ہیں:

  1. کوڈ کے چلنے سے پہلے اسکیما کا اطلاق ہوتا ہے۔ غلط ٹول کالز کو SDK کے ذریعے مسترد کر دیا جاتا ہے اور دوبارہ کوشش کی جاتی ہے۔ اس کے بجائے، خراب URLs رن ٹائم پر ناکام ہو جائیں گے۔

  2. نتائج ماڈل پر لوٹائے جاتے ہیں۔ بعد میں fileGithubIssue واپس آنے کے بعد، ماڈل نئے شمارے کا URL پڑھ سکتا ہے اور اسے سلیک جواب میں استعمال کر سکتا ہے۔ رابطے دوسرے مرحلے کو ممکن بناتے ہیں۔

  3. اسناد کو گفتگو سے خارج کر دیا گیا ہے۔ ماڈل نام سے کارروائی کی درخواست کرتا ہے، لیکن ٹوکن نہیں دیکھتا ہے۔ ٹوکن جو کبھی نہیں دیکھے جاتے ہیں ان کو تکمیل، لاگ لائنوں، یا فوری انجیکشن پے لوڈ میں لیک نہیں کیا جا سکتا۔

تیسری وجہ یہ ہے کہ اس ٹیوٹوریل کا باقی حصہ کیا بناتا ہے۔ ہم جان بوجھ کر ٹوکن کو ماڈل سے باہر رکھتے ہیں۔ ایجنٹ کے پاس شناخت کنندہ ہوتا ہے اور ٹوکن صرف فراہم کنندہ کی کال کے وقت ظاہر ہوتا ہے۔

زیادہ تر ایجنٹوں کو ایک سے زیادہ ایپ کی ضرورت ہوتی ہے۔

چند کارآمد ایجنٹ ایک ہی ایپ سے بات کرتے ہیں۔ سپورٹ نمائندے Zendesk پڑھیں اور سیلز فورس کو اپ ڈیٹ کریں۔ اسٹینڈ بائی ایجنٹ GitHub کو پڑھتے ہیں اور Slack پر شائع کرتے ہیں۔ شیڈولنگ ایجنٹ جی میل پڑھتا ہے اور گوگل کیلنڈر کو لکھتا ہے۔

ہر ایپ اپنی OAuth رجسٹریشن، دائرہ کار کا نام، ٹوکن لائف ٹائم، اور ریفریش برتاؤ فراہم کرتی ہے۔ اس فہرست کو ایجنٹ کے تمام صارفین سے ضرب دیں اور آپ کو اصل مسئلہ نظر آئے گا۔

مشترکہ ٹوکن کیوں ٹوٹے ہوئے ہیں۔

سب کے لیے ایک مشترکہ سند ڈیمو میں کام کرتی ہے اور جب دوسرا شخص ظاہر ہوتا ہے تو ناکام ہوجاتا ہے۔ ہاف سلیک کے تیز تر ورژن کا تصور کریں۔ ایک سلیک ایپ بنائیں، اسے انسٹال کریں، اور بوٹ ٹوکن حاصل کریں۔ .envیقینی بنائیں کہ تمام ٹول کالز اسے استعمال کرتی ہیں۔

تین مسائل بیک وقت پیدا ہوتے ہیں۔

سب سے پہلے، تمام پھانسیاں ایک جیسی اجازتوں کا استعمال کرتی ہیں۔ بوٹ تمام مدعو چینلز کو دیکھے گا، اس سے قطع نظر کہ اسے کس نے شروع کیا ہے۔ اگر آپ کسی ایجنٹ سے کسی ایسے چینل کے بارے میں پوچھتے ہیں جس میں اس نے کبھی حصہ نہیں لیا، تو بوٹ اسے آپ کو پڑھے گا۔ ایجنٹ آپ کے اپنے کام کی جگہ کے مراعات کو حل کرنے کا ایک طریقہ بن گئے ہیں۔

دوسرا، آڈٹ ٹریل بھی غلط ہے۔ GitHub کے تمام مسائل کہتے ہیں کہ وہ بوٹ کے ذریعہ کھولے گئے تھے۔ تمام سلیک جوابات بوٹس سے آتے ہیں۔ جب پوچھا گیا کہ مسئلہ کیوں موجود ہے، ایماندارانہ جواب ہے "ایک ایجنٹ نے کسی کے لیے مسئلہ پیش کیا، لیکن میں نہیں جانتا کہ یہ کون تھا۔”

تیسرا، منسوخی کام نہیں کرتی۔ صارف نے کمپنی چھوڑ دی ہے اور اس کا سلیک اکاؤنٹ غیر فعال کر دیا گیا ہے۔ ایجنٹ چل رہا ہے کیونکہ کوئی اسناد استعمال نہیں کی گئیں۔

ایک متبادل فی صارف گرانٹ ہے۔ ہر صارف اپنے طور پر ایپس کو منظور کرتا ہے۔ تاہم، یہ نئی ضروریات پیدا کرتا ہے. یعنی ان گرانٹس کو ذخیرہ کرنے کی جگہ۔

امتیاز فی جواب ایک فیلڈ ہے۔

سلیک اختلافات کو دیکھنا بہت آسان بناتا ہے۔ جب صارف رضامندی کی اسکرین مکمل کرتا ہے، تو ٹوکن ایکسچینج ایک ہی JSON آبجیکٹ میں دونوں قسم کے ٹوکن واپس کرتا ہے۔

{
  "ok": true,
  "access_token": "xoxb-REDACTED-BOT-TOKEN",
  "token_type": "bot",
  "authed_user": {
    "id": "U0A1B2C3D",
    "scope": "channels:history,chat:write,users:read",
    "access_token": "xoxp-REDACTED-USER-TOKEN",
    "token_type": "user"
  }
}

سب سے اوپر کی سطح access_token یہ ایک بوٹ ہے۔ گھوںسلا authed_user.access_token میں وہی ہوں جس نے ابھی اتفاق کیا۔ پڑھنا conversations.history پہلا ان تمام چینلز کو لوٹاتا ہے جن پر ایپ کو مدعو کیا گیا ہے۔ ایک سیکنڈ پڑھنا صرف وہ چینلز واپس کرتا ہے جنہیں صارف پہلے ہی دیکھ سکتا ہے۔ اسی پارٹیشن کا انتظام لکھتا ہے۔ chat.postMessage اس شخص کے نام پر ایک صارف ٹوکن پوسٹ شائع کریں۔

دو فیلڈز ہیں، سابقہ ​​میں ایک حرف کے علاوہ، اور ایجنٹ کی اجازت کا مجموعی ماڈل اس بات پر منحصر ہے کہ آپ کن فیلڈز کو اسٹور کرتے ہیں۔ اس ٹیوٹوریل میں، ہم صرف صارف کے دائرہ کار کی درخواست کر رہے ہیں، لہذا Slack کوئی بوٹ ٹوکن جاری نہیں کرتا ہے۔

ٹوکنز کو ماڈلز اور لاگز سے دور رکھا جانا چاہیے۔

صارف کے لیے مخصوص ٹوکن سسٹم میں سب سے زیادہ حساس ڈیٹا بن جاتے ہیں۔ دو منزلیں حد سے باہر ہیں:

  • ماڈل: ٹوکنز کو ان پٹ، ٹول ٹپس، اور ٹول ریٹرن ویلیوز سے خارج کریں۔ ایک ماڈل جو ٹوکن دیکھتا ہے اسے دہرا سکتا ہے، اور تیز رفتار انجیکشن ٹول کے تمام نتائج کو ناقابل اعتماد ان پٹ میں بدل دیتا ہے۔

  • آپ کا لاگ: ٹول ان پٹ اور آؤٹ پٹ وہی ہیں جو آپ ایجنٹ کو ڈیبگ کرتے وقت ریکارڈ کرنا چاہتے ہیں۔ اس پے لوڈ کے ساتھ جانے والے ٹوکن مستقل طور پر لاگ اسٹوریج میں محفوظ ہوتے ہیں۔

اس ٹیوٹوریل میں ہم ٹوکنز کو ایک تنگ راستے پر رکھیں گے۔ آپ کا کوڈ ایک شناخت کنندہ کو پاس کرتا ہے جو ایک صارف کے لیے ایک مستحکم حوالہ ہے۔ ایک مددگار اس شناخت کنندہ کو ٹوکن میں بدل دیتا ہے، اور وہاں سے ٹوکن سیدھا فراہم کنندہ کال پر جاتا ہے نہ کہ کہیں اور۔ اس کا نام ٹول اسکیما میں نہیں ہے، کسی بھی چیز سے منسلک نہیں ہے جسے ماڈل پڑھ سکتا ہے، اور اسے ٹول کے ذریعے واپس نہیں کیا جاتا ہے۔

OAuth ایپ اور اسٹور کے مالک کیوں ہیں؟

بہاؤ خود لکھنے کا نقطہ پائپنگ نہیں ہے۔ اس پر کنٹرول کریں کہ کون کس کی گرانٹ استعمال کرسکتا ہے۔

اس ٹیوٹوریل میں، آپ ٹیم کے رکن ہیں۔ ہر شخص اپنے Slack اور GitHub کو جوڑتا ہے، اور ایجنٹ اس شخص کے طور پر کام کرتا ہے جس نے عمل درآمد کو متحرک کیا۔ وہی ڈیزائن لاگو ہوتا ہے یہاں تک کہ اگر وہ صارفین آپ کی مصنوعات کے گاہک ہوں۔ ہر شخص کے پاس اب بھی اپنی اپنی اجازتیں ہیں، اور خراب نقشہ سازی کا مطلب ہے کہ ایک شخص کسی اور کے رسائی کے حقوق استعمال کر رہا ہے۔ صرف شناخت کنندہ کا ماخذ تبدیل ہوتا ہے۔ ٹیم کے ارکان کے لیے سیشن، گاہکوں کے لیے کرایہ دار کے ریکارڈ۔

فن تعمیر کا جائزہ

دو سلسلے اہم ہیں اور مختلف اوقات میں پائے جاتے ہیں۔ ان کو الگ رکھنا زیادہ تر کام ہے۔

کنکشن کے اوقات فی صارف، فی ایپ ایک بار ہوتے ہیں۔ صارف کے راضی ہونے کے بعد، ٹوکن آپ کے اسٹور پر پہنچ جائے گا۔ ایجنٹ نہیں چل رہا ہے۔

رن ٹائم ہر بار چلتا ہے. ایجنٹ ایک شناخت کنندہ کے ذریعہ موجودہ صارف کا تعین کرتا ہے اور ایک عمل انجام دیتا ہے۔ کوئی رضامندی اسکرین نہیں ہے اور نہ ہی کوئی براؤزر ہے۔

CONNECTION TIME (once per user, per app)

  Your user              connect.js              Slack / GitHub
     |                       |                         |
     |-- "connect Slack" --->|                         |
     |<--- consent link -----|                         |
     |----------------------- OAuth consent ---------->|
     |                       |<--- redirect + code ----|
     |                       |---- exchange code ----->|
     |                       |<---- tokens ------------|
     |                       |                         |
     |                  [encrypt, store                |
     |                   under (identifier,            |
     |                   provider)]                    |
     |                       |                         |


RUNTIME (every agent run)

  Your agent             Token store             Slack / GitHub
     |                       |                         |
  [resolve identifier        |                         |
   from your own session]    |                         |
     |                       |                         |
     |-- getAccessToken( --->|                         |
     |     identifier,       |                         |
     |     provider )        |                         |
     |<---- token -----------|                         |
     |                       |                         |
     |------------------ API call as user ------------>|
     |<----------------- result -----------------------|
     |                       |                         |
  [model sees result,        |                         |
   never a token]            |                         |

شکلوں کی تین خصوصیات ہیں:

شناخت کنندہ ایجنٹ کوڈ میں ٹوکن کی جگہ لے لیتا ہے۔ ٹوکن اسٹور مندرجہ بالا تمام ہینڈل سٹرنگز اس طرح: alice@example.com یا user_8f21c. تاریں بذات خود بیکار ہیں۔ اسٹوریج اور انکرپشن کلید کے بغیر، کچھ بھی نہیں کھلے گا۔

ایک ID متعدد ایپس پر محیط ہے۔ واحد شناخت کنندہ کے پاس ایک سلیک لائن اور اس کے نیچے ایک GitHub لائن ہے۔ تیسری ایپ ایڈجسٹ کرنے کے لیے تیسری آئی ڈی نہیں بنائے گی۔

کوڈ میں توثیق باقی ہے۔ اسٹور جواب دیتا ہے کہ کون سا ٹوکن شناخت کنندہ کا ہے۔ اسٹور کو معلوم نہیں ہوگا کہ آیا وہ آپ کی درخواست کا جواب دینے کے اہل ہیں یا نہیں۔ کال کرنے سے پہلے، اس بات کا تعین کریں کہ آیا کال کرنے والا اس شناخت کنندہ کے ساتھ کام کر سکتا ہے۔

ایک اصول کی پیروی کی جاتی ہے، اور اسے موڑنے سے پورے ڈیزائن کو باطل کر دیتا ہے۔ یعنی، یہ تصدیق شدہ سیشنز میں سرور سائیڈ شناخت کنندگان کی تصدیق کرتا ہے۔ درخواست کے باڈی، استفسار کے پیرامیٹرز، یا براؤزر میں شناخت کنندگان کو قبول نہ کریں۔ کلائنٹ کے ذریعہ قبول کردہ شناخت کنندہ "کسی بھی صارف کے طور پر کام کریں" اختتامی نقطہ ہے۔

سلیک اور GitHub OAuth ایپس کو کیسے رجسٹر کریں۔

یہ واک تھرو Slack اور GitHub کو دو اینڈ ٹو اینڈ فراہم کنندگان کے طور پر استعمال کرتا ہے۔ دونوں کو ایک جیسی تین چیزوں کی ضرورت ہوتی ہے: ایک رجسٹرڈ ایپ، ایک ری ڈائریکٹ URI، اور اسکوپس کا ایک سیٹ۔ تفصیلات اتنی مختلف ہیں کہ الگ سے دیکھنے کے قابل ہوں۔

ری ڈائریکٹ URI کو HTTPS استعمال کرنا چاہیے۔

OAuth کا احاطہ کرنے والے زیادہ تر سبق آپ کے لیے دستیاب ہیں۔ http://localhost:3000/callback اور چلتے رہیں۔ سلیک اس کی تردید کرتا ہے۔ سلیک کی دستاویزات میں کہا گیا ہے کہ "ری ڈائریکٹ URLs کو HTTPS بھی استعمال کرنا چاہیے،" اور اس میں کوئی استثناء نہیں ہے۔ localhost. GitHub زیادہ آرام دہ ہے اور دونوں کو اجازت دیتا ہے، لہذا ایک ہی HTTPS کال بیک دونوں کو مطمئن کرتا ہے۔

قواعد و ضوابط پیڈینٹک لگتے ہیں۔ localhost درخواست آپ کے کمپیوٹر کو کبھی نہیں چھوڑتی ہے اور لائن پر کچھ بھی نہیں روکا جاتا ہے۔ Slack بہرحال اس کو یکساں طور پر نافذ کرتا ہے، اور مستثنیات کے بغیر یکساں قوانین اسناد کو پاس کرنے والے فراہم کنندگان کے لیے قابل دفاع انتخاب ہیں۔ ہر استثنیٰ ایک برانچ ہے جسے کسی کو صحیح طریقے سے چیک کرنا ہوتا ہے، اور سوال "کیا یہ واقعی لوکل ہوسٹ ہے" ایک ایسا سوال ہے جس کا پہلے غلط جواب دیا گیا ہے۔

mkcert ایک مقامی اتھارٹی کے دستخط شدہ سرٹیفکیٹ جاری کرتا ہے اور اسے سسٹم ٹرسٹ اسٹور میں شامل کرتا ہے، لہذا براؤزر اسے بغیر وارننگ کے قبول کرتا ہے۔

mkcert -install
mkcert localhost

یہ لکھتا ہے localhost.pem اور localhost-key.pem موجودہ ڈائریکٹری پر جائیں۔ اینگروک جیسی ٹنلنگ سروسز بھی کام کرتی ہیں، لیکن چونکہ مفت یو آر ایل گھومتے ہیں، آپ کو ہر سیشن میں دونوں ایپ رجسٹریشنز کو دوبارہ ترمیم کرنے کی ضرورت ہوگی۔

سلیک ایپ اور اہم ترتیبات

api.slack.com/apps پر اپنے ورک اسپیس میں ایک ایپ بنائیں۔ پھر اسے کھولیں OAuth اور اجازتیں۔ اور دو چیزیں مقرر کرتا ہے:

شامل کریں https://localhost:3000/callback نیچے ری ڈائریکٹ URL.

پھر رینج تلاش کریں۔ صفحہ پر دو حصے ہیں، اور اگر آپ غلط سیکشن کو منتخب کرتے ہیں، تو شیئرنگ بوٹ ڈیزائن خود بخود دوبارہ بن جائے گا۔

پارٹ ٹائم نوکری کیا دیتا ہے کیا آپ اسے یہاں استعمال کرنا چاہیں گے؟
بوٹ ٹوکن رینج کوئی راستہ نہیں xoxb- ٹوکن جو ایک ایپ کے طور پر کام کرتا ہے۔ نہیں
یوزر ٹوکن رینج کوئی راستہ نہیں xoxp- ٹوکن ایک شخص کے طور پر کام کرنا ہاں

نیچے یوزر ٹوکن رینجشامل کریں:

  • channels:history: عوامی چینلز کے پیغامات پڑھیں جن سے صارف کا تعلق ہے۔

  • chat:write: بطور صارف پوسٹ کریں۔

  • users:read: صارف کی شناخت کو نام میں تبدیل کریں۔

بوٹ ٹوکن رینج کو خالی چھوڑ دیں۔ کلائنٹ آئی ڈی اور کلائنٹ سیکرٹ کاپی کریں۔ بنیادی معلومات.

GitHub OAuth ایپ

تصدیقی کال بیک URL کو سیٹنگز → ڈیولپر سیٹنگز → OAuth ایپ → نئی OAuth ایپ میں اسی پر سیٹ کریں۔ https://localhost:3000/callbackکلک کریں اور پھر کلائنٹ کا راز بنائیں۔ اگر آپ پردیی تفصیلات چاہتے ہیں تو، GitHub ویب ایپلیکیشن کے پورے بہاؤ کو دستاویز کرتا ہے۔

GitHub میں ایشو تخلیق کا دائرہ مخزن کے لحاظ سے مختلف ہوتا ہے۔

  • repo یہ آپ کے پرائیویٹ ریپوزٹری کو سنبھالتا ہے اور آپ کو اس کے ساتھ اپنے کوڈ تک پڑھنے اور لکھنے تک رسائی فراہم کرتا ہے۔

  • public_repo انتخاب زیادہ تنگ ہے، اور اگر آپ کا ٹیسٹ ریپوزٹری عوامی ہے، تو یہ کافی ہے۔

اگر ممکن ہو تو، ایک چھوٹا سا انتخاب کریں۔ وہ حدود جن کی ضرورت نہیں ہے وہ ہیں جن کی وضاحت بعد میں کرنی ہوگی۔

ماحولیاتی فائل

دونوں ایپس کلائنٹ آئی ڈی اور کلائنٹ سیکریٹ تیار کرتی ہیں، اور اسٹور کو ایک انکرپشن کلید کی ضرورت ہوتی ہے۔ پہلے ایک کلید بنائیں۔

node -e "console.log(require('node:crypto').randomBytes(32).toString('base64'))"

پھر اسے لکھیں۔ .env:

OAUTH_REDIRECT_URI=https://localhost:3000/callback
TLS_CERT_PATH=./localhost.pem
TLS_KEY_PATH=./localhost-key.pem

SLACK_CLIENT_ID=
SLACK_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

TOKEN_ENCRYPTION_KEY=

SLACK_CHANNEL_ID=C0XXXXXXXXX
GITHUB_REPO=your-name/your-test-repo

متعلقہ کلائنٹ کا راز تصدیق شدہ ہے۔ آپ کی درخواست فراہم کنندہ کو۔ یہ صارف کی اسناد نہیں ہیں اور ان کا تعلق براؤزر سے نہیں ہے۔

فی فراہم کنندہ ہر چیز کا تعلق ایک جگہ پر ہے، اس لیے بعد میں تیسرے فراہم کنندہ کو شامل کرنے کا مطلب ہے ایک غیر برانچ آئٹم کو شامل کرنا۔

مرحلہ 1: ہر فراہم کنندہ کو صرف ایک بار بیان کریں۔

const REDIRECT_URI = process.env.OAUTH_REDIRECT_URI;

export const providers = {
  slack: {
    label: 'Slack',
    authorizeUrl: 'https://slack.com/oauth/v2/authorize',
    tokenUrl: 'https://slack.com/api/oauth.v2.access',

    // These go in `user_scope`, not `scope`. Scopes listed under `scope` grant
    // a bot token, and a bot token is what this project exists to avoid.
    userScopes: ['channels:history', 'chat:write', 'users:read'],

    buildAuthorizeUrl(state) {
      const url = new URL(this.authorizeUrl);
      url.searchParams.set('client_id', process.env.SLACK_CLIENT_ID);
      url.searchParams.set('user_scope', this.userScopes.join(','));
      url.searchParams.set('redirect_uri', REDIRECT_URI);
      url.searchParams.set('state', state);
      return url.toString();
    },
    // exchangeCode and refresh follow below
  },
};

کہ user_scope ایک پیرامیٹر ایک لائن پر پوری دلیل ہے۔ سلیک پڑھیں scope بوٹ کی اجازت اور user_scope صارف کی اجازت کے لیے۔ چونکہ یہ پروجیکٹ صرف دوسرا سیٹ اپ کرتا ہے، اس لیے یہ ایسا جواب دیتا ہے جس میں بوٹ ٹوکن بالکل نہیں ہوتا ہے۔

کہ state پیرامیٹرز اختیاری نہیں ہیں۔ یہ ایک بے ترتیب تار ہے جو تیار کی جاتی ہے، سپلائر کو بھیجی جاتی ہے، اور واپسی پر چیک کی جاتی ہے۔ اس کے بغیر، انٹرنیٹ پر کوئی بھی صفحہ آپ کے براؤزر کو حملہ آور کے کال بیک URL کی طرف اشارہ کر سکتا ہے۔ code ایک بار منسلک ہونے کے بعد، سرور اس کا تبادلہ کرنے میں خوش ہوتا ہے اور حملہ آور کے ٹوکن کو صارف کے شناخت کنندہ کے نیچے محفوظ کرتا ہے۔

مرحلہ 2: اپنا کوڈ چھڑائیں اور صحیح ٹوکن حاصل کریں۔

async exchangeCode(code) {
  const response = await fetch(this.tokenUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      code,
      client_id: process.env.SLACK_CLIENT_ID,
      client_secret: process.env.SLACK_CLIENT_SECRET,
      redirect_uri: REDIRECT_URI,
    }),
  });

  const json = await response.json();

  // Slack answers HTTP 200 even when the exchange failed. The `ok` field
  // is the real status.
  if (!json.ok) {
    throw new Error(`Slack token exchange failed: ${json.error}`);
  }

  return normalizeSlackTokens(json.authed_user);
}

اگر آپ کو اس فنکشن میں کچھ تفصیلات یاد آتی ہیں، تو اس سے آپ کو ڈیبگ کرنے کا حقیقی وقت لگے گا۔

سلیک ناکامی پر HTTP 200 لوٹاتا ہے۔ چیک کر رہا ہے۔ response.ok یہ آپ کو بتاتا ہے کہ HTTP درخواست کامیاب تھی۔ کہ json.ok فیلڈ رپورٹ کرتا ہے کہ آیا OAuth ایکسچینج نے کام کیا۔

json.authed_userنہیں json. یہ اوپر والے حصے کا ایک کانٹا ہے، جس کا اظہار ایک پراپرٹی تک رسائی کے طور پر کیا گیا ہے۔ پڑھنا json.access_token یہ ٹوکن کو مرتب کرتا ہے، عملدرآمد کرتا ہے، ذخیرہ کرتا ہے، اور خاموشی سے ایجنٹ کے تمام صارفین کو ایک ہی بوٹ ID فراہم کرتا ہے۔

نتائج کو معمول پر لانا باقی کو کوڈ بیس فراہم کنندہ سے آزاد رکھتا ہے۔

function normalizeSlackTokens(authedUser) {
  return {
    accessToken: authedUser.access_token,
    refreshToken: authedUser.refresh_token ?? null,
    expiresAt: authedUser.expires_in
      ? Date.now() + authedUser.expires_in * 1000
      : null,
    scope: authedUser.scope,
  };
}

ایک ہی فنکشن کا GitHub ورژن دو قابل ذکر طریقوں سے مختلف ہے:

async exchangeCode(code) {
  const response = await fetch(this.tokenUrl, {
    method: 'POST',
    // Without this header GitHub answers with a form-encoded body.
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Accept: 'application/json',
    },
    body: new URLSearchParams({
      code,
      client_id: process.env.GITHUB_CLIENT_ID,
      client_secret: process.env.GITHUB_CLIENT_SECRET,
      redirect_uri: REDIRECT_URI,
    }),
  });

  const json = await response.json();
  if (json.error) {
    throw new Error(
      `GitHub token exchange failed: ${json.error_description ?? json.error}`
    );
  }

  // OAuth App tokens carry no expiry, so there is nothing to refresh.
  return {
    accessToken: json.access_token,
    refreshToken: null,
    expiresAt: null,
    scope: json.scope,
  };
}

کہ Accept: application/json ہیڈرز کو چھوڑنا آسان ہے اور مبہم غلطیوں کا سبب بنتا ہے۔ response.json() لوٹے ہوئے جسم پر پھینک دیں۔ access_token=gho_...&scope=repo.

مرحلہ 3: ری ڈائریکٹ کو پکڑیں۔

OAuth کو اترنے کے لیے جگہ کی ضرورت ہے۔ کمانڈ لائن ٹولز کے لیے، ایک سرور جو شروع ہوتا ہے، فی فراہم کنندہ ایک کال بیک پر کارروائی کرتا ہے، اور باہر نکلنا کافی ہے۔ کیونکہ سلیک کو HTTPS کی ضرورت ہوتی ہے۔ OAUTH_REDIRECT_URI فیصلہ کریں کہ آپ کس قسم کا سرور شروع کرنا چاہتے ہیں۔

function createCallbackServer(handler) {
  if (redirect.protocol !== 'https:') {
    return createHttpServer(handler);
  }

  try {
    return createHttpsServer(
      {
        cert: readFileSync(process.env.TLS_CERT_PATH),
        key: readFileSync(process.env.TLS_KEY_PATH),
      },
      handler
    );
  } catch (err) {
    throw new Error(
      `Could not read the TLS certificate (${err.code ?? err.message}).n` +
        'Generate a locally-trusted one with mkcert:n' +
        '  mkcert -installn' +
        '  mkcert localhostn' +
        'then point TLS_CERT_PATH and TLS_KEY_PATH at the two files it writes.'
    );
  }
}

گمشدہ سرٹیفکیٹس کسی کے پاس ہوں گے۔ ENOENT یہ اپنے آپ میں OAuth کے بارے میں کچھ نہیں بتاتا ہے۔ کیچ بلاک اس کے بجائے چار لائنیں لیتا ہے کہ اسے کیا کرنا ہے۔

خود ہینڈلر کہاں ہے؟ state ٹیسٹ کروائیں:

const pending = new Map();

function handleCallback(request, response) {
  const url = new URL(request.url, redirect.origin);

  if (url.pathname !== redirect.pathname) {
    response.writeHead(404).end('Not found');
    return;
  }

  const state = url.searchParams.get('state');
  const entry = pending.get(state);

  if (!entry) {
    response.writeHead(400).end('State mismatch. Start the flow again.');
    return;
  }

  pending.delete(state);

  const error = url.searchParams.get('error');
  if (error) {
    response.writeHead(400).end(`Authorization denied: ${error}`);
    entry.reject(new Error(`[${entry.provider}] authorization denied: ${error}`));
    return;
  }

  entry.finish(url.searchParams.get('code'), response);
}

کہ pending نقشہ ہے state چیک کریں ریاستی اقدار نقشہ میں صرف اس صورت میں داخل ہوتی ہیں جب وہ اس عمل کے ذریعے تخلیق کی گئی ہوں اور ان کے استعمال کے وقت حذف ہو جائیں۔ ایک غیر تسلیم شدہ حالت کا مطلب ہے کہ کال بیک اس بہاؤ سے نہیں آیا جس نے اسے شروع کیا تھا، اور جو ریاست دو بار آتی ہے اس کا مطلب ہے کہ اسے دوبارہ چلایا جاتا ہے۔ دونوں ایک سے گھٹائیں Map چیک کریں

ریاست بنانا اور کال بیک کا انتظار کرنا:

function connect(providerName) {
  const provider = providers[providerName];
  const state = randomBytes(16).toString('hex');

  console.log(`n[${providerName}] authorize as "${IDENTIFIER}":`);
  console.log(provider.buildAuthorizeUrl(state));

  return new Promise((resolve, reject) => {
    pending.set(state, {
      provider: providerName,
      reject,
      async finish(code, response) {
        const tokens = await provider.exchangeCode(code);
        saveGrant(IDENTIFIER, providerName, tokens);
        response
          .writeHead(200, { 'Content-Type': 'text/html' })
          .end(`

${provider.label} connected. You can close this tab.

`); resolve(); }, }); }); }

randomBytes(16) اور پھر نہیں۔ Math.random(). قابل پیشن گوئی ریاست کے پیرامیٹرز بغیر کسی ریاستی پیرامیٹرز کے برابر ہیں۔

اس کو چلانے سے آپ ہر منقطع فراہم کنندہ کے ذریعے بدلے میں لے جائیں گے۔

[slack] authorize as "alice@example.com":
https://slack.com/oauth/v2/authorize?client_id=123.456&user_scope=channels%3Ahistory%2Cchat%3Awrite%2Cusers%3Aread&redirect_uri=https%3A%2F%2Flocalhost%3A3000%2Fcallback&state=1159699dbf1a808fd33ba31c7b643505

چیک کریں کہ اس URL میں کیا شامل نہیں ہے۔ scope پیرامیٹرز سلیک کو بوٹ ٹوکن جاری کرنے کی ہدایت نہیں کی گئی ہے، لہذا ہم ایسا نہیں کریں گے۔

ایک خفیہ کردہ ٹوکن کو ذخیرہ کرنے کے لیے صارف کس طرح ایک کلید داخل کرتے ہیں۔

اسٹور ایک سوال کا جواب دیتا ہے: اس فراہم کنندہ کے لیے اس صارف کے کون سے ٹوکنز ہیں؟ اس کے بارے میں باقی سب کچھ ان جوابات کو محفوظ رکھنے سے آتا ہے۔

node:sqlite اسے نوڈ کے ساتھ v22.5 سے بھیج دیا گیا ہے، اور v22.13 میں جھنڈے کی ضرورت نہیں ہے۔ پھر آپ اپنے اصل ڈیٹا بیس کو بغیر کچھ انسٹال کیے استعمال کر سکتے ہیں۔

import { DatabaseSync } from 'node:sqlite';
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';

const KEY = Buffer.from(process.env.TOKEN_ENCRYPTION_KEY ?? '', 'base64');

if (KEY.length !== 32) {
  throw new Error(
    'TOKEN_ENCRYPTION_KEY must be 32 bytes, base64-encoded. ' +
      `Got ${KEY.length} bytes.`
  );
}

const db = new DatabaseSync(
  process.env.TOKEN_DB_PATH ?? new URL('../tokens.db', import.meta.url).pathname
);

// One row per user, per provider. expires_at stays outside the ciphertext so
// a token's freshness can be checked without decrypting it.
db.exec(`
  CREATE TABLE IF NOT EXISTS grants (
    identifier TEXT    NOT NULL,
    provider   TEXT    NOT NULL,
    ciphertext BLOB    NOT NULL,
    iv         BLOB    NOT NULL,
    auth_tag   BLOB    NOT NULL,
    expires_at INTEGER,
    PRIMARY KEY (identifier, provider)
  )
`);

ایک جامع بنیادی کلید ایک ریکارڈ شدہ تنہائی کی ضمانت ہے۔ (identifier, provider) اس کا مطلب یہ ہے کہ ایلس کی سلیک قطار اور باب کی سلیک قطار متصادم نہیں ہوسکتی ہے، اور ایک استفسار جو دونوں حصوں کو فراہم کرتا ہے دوسرے کی طرف سے ایک اعتراف واپس نہیں کرسکتا۔

expires_at یہ جان بوجھ کر سائفر ٹیکسٹ سے باہر رکھا گیا ہے۔ چیک کرنا کہ آیا ٹوکن کو ریفریش کرنے کی ضرورت ہے جو ہم ہر کال سے پہلے کرتے ہیں۔ تلاش کرنے کے لیے ڈکرپٹ کرنے کا مطلب ہے مسلسل ڈکرپٹ کرنا، اس لیے ایک غیر خفیہ فیلڈ پڑھنے کے قابل رہتا ہے۔

خفیہ کاری AES-256-GCM ہے اور تصدیق کرتا ہے اور خفیہ کرتا ہے:

function encrypt(payload) {
  const iv = randomBytes(12);
  const cipher = createCipheriv('aes-256-gcm', KEY, iv);
  const ciphertext = Buffer.concat([
    cipher.update(JSON.stringify(payload), 'utf8'),
    cipher.final(),
  ]);
  return { ciphertext, iv, authTag: cipher.getAuthTag() };
}

function decrypt({ ciphertext, iv, authTag }) {
  const decipher = createDecipheriv('aes-256-gcm', KEY, iv);
  decipher.setAuthTag(authTag);
  const plaintext = Buffer.concat([
    decipher.update(ciphertext),
    decipher.final(),
  ]);
  return JSON.parse(plaintext.toString('utf8'));
}

وہ جوڑا تین اصولوں کے ساتھ مشروط ہے، اور ان میں سے کسی کو توڑنا بالکل بھی خفیہ نہ کرنے سے بدتر ہے۔ کیونکہ یہ کامیابی کی طرح لگتا ہے۔

  1. نیا IV فی کریپٹو: GCM کے ساتھ انیشیلائزیشن ویکٹر کو دوبارہ استعمال کرنا کوئی معمولی غلطی نہیں ہے بلکہ ایک مہلک غلطی ہے۔ randomBytes(12) یہ ہر کال پر سائفر ٹیکسٹ کے ساتھ محفوظ ہوتا ہے۔

  2. براہ کرم تصدیق کا ٹیگ رکھیں۔ GCM ایسے ٹیگز تیار کرتا ہے جو ثابت کرتے ہیں کہ سائفر ٹیکسٹ کو تبدیل نہیں کیا گیا ہے۔ بغیر setAuthTag واپسی پر، دیانتداری کے بغیر خفیہ کاری کی گئی۔ decipher.final() میں شکایت نہیں کروں گا۔

  3. ہر فیلڈ کے بجائے پورے ٹوکن آبجیکٹ کو خفیہ کرتا ہے۔ کے لیے ایک سیفر ٹیکسٹ { accessToken, refreshToken, scope } اس کا مطلب ہے کہ آپ کو تین کے بجائے ایک IV اور ایک ٹیگ کا انتظام کرنا ہوگا۔

اس سے لکھنا اور پڑھنا کم نمایاں ہو جائے گا۔

export function saveGrant(identifier, provider, tokens) {
  const { ciphertext, iv, authTag } = encrypt(tokens);
  db.prepare(
    `INSERT INTO grants (identifier, provider, ciphertext, iv, auth_tag, expires_at)
     VALUES (?, ?, ?, ?, ?, ?)
     ON CONFLICT (identifier, provider) DO UPDATE SET
       ciphertext = excluded.ciphertext,
       iv         = excluded.iv,
       auth_tag   = excluded.auth_tag,
       expires_at = excluded.expires_at`
  ).run(identifier, provider, ciphertext, iv, authTag, tokens.expiresAt ?? null);
}

کہ ON CONFLICT دفعات ان کی نظر سے کہیں زیادہ اہم ہیں۔ دوبارہ رضامندی کو اجازت کی جگہ لے لینی چاہیے، اسے ناکام یا نقل نہیں کرنا چاہیے، اور دوبارہ رضامندی بالکل وہی ہے جو صارف منسوخی یا دائرہ کار میں تبدیلی کے بعد کرتا ہے۔

خفیہ کاری کی کلید خود ہے۔ .env یہ ٹیوٹوریلز کے لیے موزوں ہے نہ کہ سیکرٹ مینیجر یا KMS کے حصے کے طور پر پروڈکشن کے لیے۔ اگر آپ اسے کھو دیتے ہیں تو، تمام ذخیرہ شدہ اجازتیں ناقابل پڑھی جائیں گی اور تمام صارفین کو دوبارہ رضامندی دینا ہوگی۔ یہ ایک حقیقی بندش ہے، لیکن یہ چوری شدہ ڈیٹا بیس فائل سے بہتر ہے کہ ایجنٹ کے تمام صارفین کو ورکنگ ٹوکن دے دیا جائے۔

رن ٹائم میں تین کام ہوتے ہیں: شناخت کنندہ کو حل کریں، ٹوکن حاصل کریں، اور پوری چیز کو ایک ٹول میں لپیٹیں۔

مرحلہ 1: شناخت کنندہ کی تصدیق کریں اور منظوری دیں۔

شناخت کنندگان یہ ہیں: مستحکم تار یہ ایک صارف، ای میل ایڈریس، صارف ID، اور کرایہ دار اسکوپ کلید کی نمائندگی کرتا ہے۔

// In a real app this comes from your authenticated session, resolved
// server-side. Never accept it from client input.
const IDENTIFIER = process.argv[2] ?? 'channel-watcher-agent';

شناخت کنندہ پڑھیں argv لاگ ان کیے بغیر ڈیمو کو چلانے کے قابل رکھیں اور اس ٹیوٹوریل میں بعد میں الگ تھلگ ٹیسٹوں کو ایک ہی کمانڈ بنائیں۔ اصل ایپلیکیشن درج ذیل لائنوں کی جگہ لے لے گی۔

// Real app: resolve from your authenticated session, server-side.
const session = await getSession(request);                  // your auth
const identifier = await lookupIdentifier(session.userId);  // your database

ان دو سطروں کی ترتیب اہم ہے۔ پہلے بھیجنے والے کی تصدیق کریں، پھر معلوم کریں کہ بھیجنے والا کس شناخت کنندہ کے طور پر کام کرے گا۔ کلائنٹ سے آنے والے شناخت کنندگان اختتامی نقطہ کو تمام صارفین کے لیڈر میں تبدیل کر دیتے ہیں۔

مرحلہ 2: شناخت کنندگان کو دیر سے ٹوکنز میں تبدیل کریں۔

شناخت کنندہ اور ہر فراہم کنندہ کال کے درمیان ایک فنکشن ہوتا ہے۔

const REFRESH_WINDOW_MS = 60_000;

export async function getAccessToken(identifier, providerName) {
  const grant = readGrant(identifier, providerName);

  if (!grant) {
    throw new Error(
      `[${providerName}] no grant for "${identifier}".n` +
        `Connect it first: node src/connect.js ${identifier}`
    );
  }

  const expiringSoon =
    grant.expiresAt !== null &&
    grant.expiresAt !== undefined &&
    grant.expiresAt - Date.now() < REFRESH_WINDOW_MS;

  if (!expiringSoon) {
    return grant.accessToken;
  }

  if (!grant.refreshToken) {
    throw new Error(
      `[${providerName}] token for "${identifier}" expired and no refresh ` +
        'token is stored. The user has to consent again.'
    );
  }

  const refreshed = await providers[providerName].refresh(grant.refreshToken);
  saveGrant(identifier, providerName, refreshed);
  return refreshed.accessToken;
}

اس کال کو اپنی API کال سے فوراً پہلے کال کریں، بجائے اس کے کہ ایک بار شروع ہونے پر۔ ایجنٹ کی لمبی دوڑیں 12 گھنٹے کے ٹوکن سے زیادہ دیر تک چل سکتی ہیں، اور ٹوکن کو وقت سے پہلے حل کرنے کا مطلب ہے کہ انہیں انتہائی تکلیف دہ لمحے میں دریافت کرنا۔ دیر سے بازیافت کرنے سے آپ کو ایک سستا ڈیٹا بیس پڑھنا پڑتا ہے اور پوری کلاس کے مسائل کو ختم کر دیتا ہے۔

بھی، 60 سیکنڈ ونڈو خود پیڈ نہیں ہے۔ 4 سیکنڈ باقی رہنے والے ٹوکن کی معیاد ایک سادہ معیاد چیک پاس کرنے کے بعد پرواز میں ختم ہو جاتی ہے۔ ونڈو کے اندر آئٹمز کو ریفریش کرنے کا مطلب ہے کہ لوٹا ہوا ٹوکن کم از کم ایک منٹ کے لیے آپ کے کام کے لیے اچھا رہے گا۔

آخر میں، اگر کوئی گرانٹ غائب ہے، تو پیچھے ہٹنے کے بجائے ایک غلطی اٹھائی جائے گی۔ پیچھے ہٹنا کوئی معنی نہیں رکھتا۔ غیر مربوط صارف کے لیے درست نتیجہ ایک فریز ہے جس میں ایک پیغام ہے جس میں بتایا گیا ہے کہ کیسے جڑنا ہے۔

مرحلہ 3: فراہم کنندہ کال کو ایک ٹول سے لپیٹیں۔

ID کو ایک پرت کے نیچے انجکشن کیا جاتا ہے جس پر ماڈل اثر انداز ہو سکتا ہے۔

export function buildTools(identifier) {
  const [owner, repo] = process.env.GITHUB_REPO.split('/');

  const fileGithubIssue = tool({
    description: 'File a GitHub issue for an actionable Slack message',
    inputSchema: z.object({
      title: z.string(),
      body: z.string(),
    }),
    execute: async ({ title, body }) => {
      const token = await getAccessToken(identifier, 'github');
      return createIssue(token, owner, repo, { title, body });
    },
  });

  const replyInSlackThread = tool({
    description:
      'Reply in the original Slack thread (e.g. with the created issue link)',
    inputSchema: z.object({
      text: z.string(),
      thread_ts: z.string(),
    }),
    execute: async ({ text, thread_ts }) => {
      const token = await getAccessToken(identifier, 'slack');
      return postThreadReply(
        token,
        process.env.SLACK_CHANNEL_ID,
        text,
        thread_ts
      );
    },
  });

  return { fileGithubIssue, replyInSlackThread };
}

موازنہ کریں کہ ماڈل کیا کنٹرول کرتا ہے اور کیا کنٹرول نہیں کر سکتا۔ ماڈل منتخب کرتا ہے title، bodyاور text. ماڈل صارفین کو منتخب نہیں کر سکتا۔ identifier ماڈل پر عمل درآمد سے پہلے بندش کے دلائل میں ترمیم کی گئی، inputSchema. چونکہ آپ کا اکاؤنٹ ان پٹس میں سے ایک نہیں ہے، اس لیے آپ کے پاس کوئی ان پٹ نہیں ہے جو دوسروں کی طرح آپ کی ماڈل فائل کے ساتھ مسائل کا باعث بن رہے ہیں۔

ہر واپسی کی قیمت کے لیے ایک آڈٹ کی ضرورت ہوتی ہے۔ createIssue شمارہ نمبر، URL اور عنوان لوٹاتا ہے۔ postThreadReply ٹائم اسٹیمپ لوٹاتا ہے۔ نہ تو کوئی ٹوکن لوٹاتا ہے اور نہ ہی یہ فراہم کنندہ کا خام جواب واپس کرتا ہے جسے ٹوکن چھپا سکتا ہے۔

فراہم کنندہ کال خود سادہ HTTP ہے۔

export async function createIssue(token, owner, repo, { title, body }) {
  const response = await fetch(
    `https://api.github.com/repos/${owner}/${repo}/issues`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: 'application/vnd.github+json',
        'X-GitHub-Api-Version': '2022-11-28',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ title, body }),
    }
  );

  const json = await response.json();

  if (!response.ok) {
    // 403 here usually means the grant is missing the `repo` scope.
    throw new Error(
      `GitHub issue creation failed (${response.status}): ${json.message}`
    );
  }

  return { number: json.number, url: json.html_url, title: json.title };
}

مرحلہ 4: چینل پڑھیں

سلیکس conversations.history ایک جگہ کے ساتھ صاف JSON لوٹاتا ہے۔ پیغام میں آپ کا صارف ID شامل ہے، آپ کا ڈسپلے نام نہیں۔ اسے نام میں تبدیل کرنا ہے۔ users.info براہ کرم ہر ایک کو کال کریں۔ users:read یہ دائرہ کار کی فہرست میں تھا۔

export async function readChannel(token, channelId, limit = 20) {
  const { messages } = await slackCall(token, 'conversations.history', {
    channel: channelId,
    limit: String(limit),
  });

  const authors = await resolveAuthors(
    token,
    messages.filter((m) => m.user).map((m) => m.user)
  );

  return messages
    .filter((message) => message.text)
    .map((message) => ({
      author: authors.get(message.user) ?? 'unknown',
      userId: message.user,
      text: message.text,
      ts: message.ts,
    }))
    .reverse(); // oldest first
}

اس خصوصیت میں تین چھوٹے فیصلے:

  1. نام کی قیمت 1۔ users.info منفرد مصنف کے ذریعہ بلایا گیا۔ فی رن کیشنگ اس بات کو یقینی بناتی ہے کہ ایک شخص کے پیغامات سے بھرا ہوا چینل 20 ایک جیسی ہٹس پیدا نہیں کرتا ہے۔ چونکہ ناقابل حل نام عمل کو ترک کرنے کی وجہ نہیں ہے، اس لیے کوئی ناکام تلاش پیدا نہیں ہوتی ہے اور صارف ID پر واپس نہیں جاتی ہے۔

  2. کوئی پیغام نہیں text اتر جاؤ چینل جوائن کرتا ہے اور دوبارہ کام کرتا ہے میسج آبجیکٹ کے طور پر بغیر کسی باڈی کے آتے ہیں، اور ماڈل کے لیے ان کی درجہ بندی کرنے کے لیے کچھ بھی نہیں ہے۔

  3. .reverse() یہ کاسمیٹک نہیں ہے۔ سلیک سب سے پہلے تازہ ترین آئٹمز واپس کرتا ہے۔ وہ ماڈل جو گفتگو کو پیچھے کی طرف پڑھتے ہیں وہ غلط پڑھتے ہیں کہ کس پیغام کا جواب دیا گیا۔

کہ ts فیلڈ ڈبل ڈیوٹی کھینچتا ہے۔ یہ پیغام کی شناخت کرتا ہے اور جوابات کے لیے تھریڈ اینکر کے طور پر کام کرتا ہے اور ایجنٹ کے لیے یہ یاد رکھنے کے لیے ایک کلید کا کام کرتا ہے کہ اس نے پہلے سے کیا کارروائی کی ہے۔

const state = await loadState();
const processed = new Set(state[IDENTIFIER]?.processedTs ?? []);
const newMessages = messages.filter((m) => !processed.has(m.ts));

ایک کلید جو ریاست کی بطور شناخت کنندہ نمائندگی کرتی ہے۔بالکل ٹکڑا کی طرح۔ ایک سادہ فہرست کا استعمال ایک صارف کے پروسیس شدہ پیغامات کو دوسرے صارفین کے پیغامات کو چھپانے کی اجازت دیتا ہے، جو صارفین کے درمیان ایک جگہ پر خون بہنے کو دوبارہ پیش کرتا ہے، جسے روکنے کے لیے پورا ڈیزائن موجود ہے۔

مرحلہ 5: ٹول لوپ چلائیں۔

دونوں ٹولز کو ماڈل میں منتقل کریں اور ماڈل کو فیصلہ کرنے دیں۔

const { text } = await generateText({
  model: anthropic(process.env.MODEL),
  tools,
  stopWhen: stepCountIs(5),
  prompt: `You triage messages from a dev team's Slack channel.

Message from ${message.author}: "${message.text}"
Message timestamp (thread_ts): ${message.ts}

Decide if this message is actionable (a bug report or concrete action item) or just noise (chit-chat, join notices, already-resolved chatter).

If actionable: file a GitHub issue with a clear title and body drafted from the message, then reply in the original Slack thread (use the exact thread_ts above) with a short note and the created issue's URL.

If not actionable: do nothing and briefly say why.`,
});

کہ انگوٹھی یہی وہ چیز ہے جو دوسرا مرحلہ ممکن بناتی ہے۔ ماڈل پیغامات پڑھ سکتا ہے اور کال کر سکتا ہے۔ fileGithubIssue. AI SDK ٹول چلاتا ہے، نتائج کو ایک نئے مسئلے والے URL کے ساتھ سیاق و سباق میں فیڈ کرتا ہے، اور ماڈل کو دوبارہ کال کرتا ہے۔ ماڈل اب ایک ایسے URL کا استعمال کرتے ہوئے تھریڈ میں جواب دے سکتا ہے جو پہلے مرحلے میں نامعلوم تھا۔ پھر رک جاتا ہے۔

stopWhen: stepCountIs(5) راؤنڈ بند کریں۔ حدود کے بغیر، ایک افراتفری والا ماڈل ایک ناکام ٹول کو غیر معینہ مدت تک دوبارہ کوشش کرنے کا سبب بن سکتا ہے۔ دو ٹولز کے لیے پانچ چکر کافی ہیں۔

deterministic ورژن بھی معقول ہے۔ ہم اسے سٹرکچرڈ آؤٹ پٹ کالز میں توڑ دیتے ہیں اور پھر دونوں ٹولز کو براہ راست ایک مقررہ ترتیب میں کال کرتے ہیں جب پیغام سمجھ میں آتا ہے۔

فکسڈ تسلسل کو جانچنا اور حقیقی لچک کو ترک کرنا آسان ہوتا ہے۔ لوپس ماڈل کو جواب چھوڑنے یا جواب کے بغیر فائل جمع کرانے کی اجازت دیتے ہیں، اور تیسرے ٹول کو شامل کرنے سے نئی برانچ کی ضرورت ختم ہوجاتی ہے۔ اگر آپریشنز کا سیٹ ان پٹ کے لحاظ سے مختلف ہوتا ہے، تو ایک لوپ کا انتخاب کریں۔ دوسری صورت میں، ایک مقررہ آرڈر کا انتخاب کریں.

پھانسی کی اشیاء کی درستگی کو یقینی بنانے کے لیے سپلائر لائنوں پر نوٹس۔ اوپر کا ٹکڑا ہے۔ @ai-sdk/anthropicیہ براہ راست Anthropic API کیز کے لیے موزوں ہے۔ میری جانچ ایک OpenAI ہم آہنگ گیٹ وے کے ذریعے کی گئی تھی جو صرف فراہم کنندہ کی ترتیب کو تبدیل کرتی ہے۔

import { createOpenAICompatible } from '@ai-sdk/openai-compatible';

const gateway = createOpenAICompatible({
  name: 'gateway',
  baseURL: `${process.env.GATEWAY_BASE_URL}/v1`,
  apiKey: process.env.GATEWAY_API_KEY,
});
// then: model: gateway(process.env.MODEL)

ٹولز، لوپس، اور ٹوکن ہینڈلنگ دونوں طرح سے ایک جیسے ہیں۔ صرف model دلیل بدل جاتی ہے۔

ریفریشز اور ٹرمینیشنز کو کیسے ہینڈل کریں۔

ٹوکن دو مختلف طریقوں سے ختم ہوتے ہیں: ان میں سے صرف ایک کوڈ کے ساتھ مسئلہ ہے**.** میعاد ختم ہونا معمول ہے اور قابل بازیافت ہے۔ دستبرداری ایک فیصلہ ہے جو کسی نے کیا ہے، اور درست جواب دوبارہ رضامندی حاصل کرنا ہے۔

اس ٹیوٹوریل میں دو فراہم کنندگان سپیکٹرم کے مخالف سروں پر ہیں، انہیں ایک مفید جوڑا بناتا ہے۔

GitHub: ٹوکن جو کبھی ختم نہیں ہوتا ہے۔

OAuth ایپ صارف ٹوکن میں میعاد ختم ہونے کا ٹائم اسٹیمپ نہیں ہوتا ہے۔ ذخیرہ کرنے کے لیے کوئی ریفریش ٹوکن نہیں ہیں اور نہ ہی ریفریش کالز ہیں۔ یہی وجہ ہے۔ github.refresh() یہ منصوبہ صرف خود وضاحتی ہے۔

async refresh() {
  throw new Error(
    'GitHub OAuth App tokens do not expire. A failure here means the ' +
      'grant was revoked — send the user through consent again.'
  );
}

"کبھی ختم نہیں ہوتا" "ہمیشہ کے لئے رہتا ہے" جیسا نہیں ہے، اور GitHub چند وجوہات کی بنا پر ٹوکن کو منسوخ کرتا ہے جو جاننے کے قابل ہے۔

  • صارف اکاؤنٹ کی ترتیبات میں اجازت منسوخ کرتا ہے۔

  • ٹوکن ایک سال تک استعمال نہیں کیے جائیں گے۔

  • ٹوکن کو پبلک ریپوزٹری یا Gist کی طرف دھکیل دیا جاتا ہے، جس مقام پر GitHub خود بخود ٹوکن کو منسوخ کر دیتا ہے۔

  • ایپ ایک ہی صارف اور دائرہ کار کے امتزاج کے لیے 10 سے زیادہ ٹوکن جمع کرتی ہے، اور سب سے پرانا ٹوکن منسوخ کر دیا جاتا ہے۔

تیسرا ایک لمحہ گزارنے کے قابل ہے۔ GitHub اپنے ٹوکن فارمیٹ کے لیے عوامی دباؤ کو تلاش کرتا ہے اور جو کچھ بھی ملتا ہے اسے ختم کر دیتا ہے۔ یہ ایک حفاظتی جال ہے، حکمت عملی نہیں، اور ایک چیز جس کی حفاظت نہیں کر سکتی وہ ہے نجی اسٹوریج یا لاگ فائلوں میں ٹوکن۔

GitHub ایپس OAuth ایپس سے مختلف برتاؤ کرتی ہیں۔GitHub دستاویزات کو پڑھتے وقت یہ الجھن کا ایک عام ذریعہ ہے۔ GitHub ایپس کے لیے صارف تک رسائی کے ٹوکن 8 گھنٹے بعد ختم ہو جاتے ہیں اور ایک ریفریش ٹوکن کے ساتھ آتے ہیں جو 6 ماہ کے لیے درست ہے۔ اگر آپ اس کے بجائے GitHub ایپس میں تعمیر کر رہے ہیں، تو نیچے سلیک کی شکل کا ریفریش پاتھ وہ راستہ ہے جو آپ چاہتے ہیں۔

سلیک: تبدیلی اختیاری اور مستقل ہے۔

پہلے سے طے شدہ طور پر، سلیک صارف ٹوکن بھی کبھی ختم نہیں ہوتے ہیں۔ ٹوکن کی گردش اس صورت حال کو تبدیل کرتی ہے اور دہرانے کے قابل ایک انتباہ پیدا کرتی ہے۔ ایک بار آن ہونے کے بعد، گردش کو بند نہیں کیا جا سکتا۔ پہلے اسے اپنی ٹیسٹ ایپ میں چالو کریں۔

گھومنے کے فعال ہونے کے بعد، ٹوکنز 12 گھنٹے کے لیے رکھے جائیں گے اور ریفریش ٹوکن کے ساتھ آئیں گے۔ ریفریش کال ایک مختلف اجازت کی قسم کا استعمال کرتے ہوئے، ابتدائی ایکسچینج کے طور پر اسی اختتامی نقطہ کو دوبارہ استعمال کرتی ہے۔

async refresh(refreshToken) {
  const response = await fetch(this.tokenUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: process.env.SLACK_CLIENT_ID,
      client_secret: process.env.SLACK_CLIENT_SECRET,
    }),
  });

  const json = await response.json();
  if (!json.ok) {
    throw new Error(`Slack token refresh failed: ${json.error}`);
  }

  return normalizeSlackTokens(json.authed_user ?? json);
}

نئے ریفریش ٹوکن کے ساتھ ساتھ نئے رسائی ٹوکن کو بھی محفوظ کریں۔ ریفریش ٹوکن بھی گھمائے جاتے ہیں۔ اگر آپ صرف رسائی ٹوکن کو دوبارہ بناتے ہیں، تو آپ کے پاس خرچ شدہ ریفریش ٹوکن رہ جائے گا اور یہ 12 گھنٹے کے بعد ناکام ہو جائے گا، اس لیے کچھ سیکھنے کے لیے انتظار کرنے کے لیے کافی وقت ہے۔

اس لیے میں یہ جائزہ لکھ رہا ہوں۔ getAccessToken فون کال saveGrant ٹوکن واپس کرنے اور جاری رکھنے کے بجائے ریفریش کرنے کے بعد۔

ڈیڈ گرانٹ کے ساتھ نارمل سلوک کیا گیا۔

منسوخ شدہ گرانٹ غیر معمولی معنوں میں مستثنیٰ نہیں ہے۔ صارفین چلے جاتے ہیں، منتظمین اپنا دائرہ کم کر لیتے ہیں، اور لوگ اپنا ذہن بدل لیتے ہیں کہ ایجنٹ کیا کر سکتے ہیں۔

یہاں یہ ہے کہ یہ عمل میں کیسا لگتا ہے: getAccessToken پہلے سے ہی استعمال میں ہے: غلطیاں پکڑیں ​​اور اسٹیک ٹریس کی بجائے نیا تصدیقی لنک دکھائیں۔ connect.js ایک ہی شناخت کنندہ کا استعمال صارف کو دوبارہ آپٹ ان کرنے کی اجازت دے گا۔ ON CONFLICT مردہ قطاریں اوور رائٹ ہو جاتی ہیں اور صارف کے ریکارڈ میں کوئی اور چیز تبدیل نہیں ہوتی ہے۔

دوسرا فراہم کنندہ کیسے شامل کریں۔

دوسرے فراہم کنندہ کی قیمت 1 OAuth ایپ، 1 فراہم کنندہ آبجیکٹ آئٹم، اور 1 ٹول ہے۔ ID کو ایک ہی تار کے طور پر رکھنے سے آپ کو رعایت ملتی ہے۔

ایجنٹ نے دو فراہم کنندگان کا استعمال جاری رکھا۔ قابل ذکر بات یہ ہے کہ دوسری ID کی ضرورت نہیں تھی، دوسرے رضامندی کے سرور کی ضرورت نہیں تھی، اور کوئی دوسرا ٹوکن ٹیبل نہیں تھا۔

export const providers = {
  slack: { /* ... */ },
  github: { /* ... */ },
};

تیسرا، گوگل کیلنڈر اس کا اپنا ایک تہائی حصہ ہے۔ authorizeUrl، tokenUrlرینج اور exchangeCode. رضامندی کا سرور ختم ہو گیا۔ Object.keys(providers)بغیر کسی ترمیم کے ایک نیا آئٹم منتخب کریں۔ اسٹور پہلے ہی فعال ہے۔ (identifier, provider)اس لیے ہجرت کی ضرورت نہیں۔ پھر ہمارے پاس ایک اور ٹول ہے۔

const createCalendarEvent = tool({
  description: 'Create a calendar event',
  inputSchema: z.object({ summary: z.string(), start: z.string() }),
  execute: async ({ summary, start }) => {
    const token = await getAccessToken(identifier, 'google-calendar');
    // ...one more provider call
  },
});

شناخت کنندگان تبدیل نہیں ہوتے ہیں، صارف کی میزیں تبدیل نہیں ہوتی ہیں، اور ماڈل کا ورلڈ ویو بالکل ایک ٹول سے بڑھتا ہے۔

وہ اخراجات جن کو کم نہیں کیا جا سکتا فراہم کنندہ کے لیے مخصوص علم۔ ہر نئے فراہم کنندہ کا اپنا دائرہ کار، اس کی اپنی غلطی کی اقسام، اور اس کے اپنے جوابات ہیں کہ آیا ٹوکن کی میعاد ختم ہو گئی ہے۔ سلیک اور گٹ ہب تینوں پر متفق نہیں تھے، اور شاید کسی تیسرے پر متفق نہیں ہوں گے۔ رجسٹری پیٹرن اس علم کو ایجنٹوں کے ذریعے پھیلانے کے بجائے فی فراہم کنندہ کے ایک شے میں سرایت کرتا ہے، لیکن یہ علم کو غیر ضروری نہیں بناتا ہے۔

رضامندی کے بارے میں ایک انتباہ: سبسڈیز صارف کے لیے مخصوص اور فراہم کنندہ کے لیے مخصوص ہیں۔ ایلس سلیک کو جوڑ رہی ہے لیکن کیلنڈر نہیں اس کا مطلب ہے کہ کیلنڈر ٹول پر اس کی کال ناکام ہوگئی، اور چونکہ اس نے رضامندی نہیں دی، اس لیے ناکامی یہاں درست ہے۔ اسے ایک پیغام کے طور پر سمجھیں جس میں آپ کو غلطی کی بجائے رابطہ قائم کرنے کے لیے کہا گیا ہے۔ ایرر موڈ سیکشن دوبارہ ظاہر ہوتا ہے۔

مکمل واک تھرو

ذخیرہ کلون کریں، انسٹال کریں اور درج کریں۔ .env:

git clone https://github.com/saif-shines/channel-watcher-agent.git
cd channel-watcher-agent
npm install
cp .env.example .env
# fill in both client IDs and secrets, the encryption key, channel ID, repo

پھر جڑیں۔ یہ کمانڈ کال بیک سرور کو شروع کرتی ہے اور غیر منسلک فراہم کنندہ کے لیے ایک لنک پرنٹ کرتی ہے۔

npm run connect
[slack] authorize as "channel-watcher-agent":
https://slack.com/oauth/v2/authorize?client_id=123.456&user_scope=channels%3Ahistory%2Cchat%3Awrite%2Cusers%3Aread&redirect_uri=https%3A%2F%2Flocalhost%3A3000%2Fcallback&state=1159699dbf1a808fd33ba31c7b643505
[slack] connected.

[github] authorize as "channel-watcher-agent":
https://github.com/login/oauth/authorize?client_id=Iv1.abc&scope=repo&redirect_uri=https%3A%2F%2Flocalhost%3A3000%2Fcallback&state=e6d13461099c391367266235f8313630
[github] connected.

All providers connected. Run: node src/index.js channel-watcher-agent

ہر لنک کو کھولیں اور ٹیب کی تصدیق کے لیے اسے قبول کریں۔ واپسی کے راستے میں اس URL کے اسٹیٹس پیرامیٹرز کو چیک کیا جاتا ہے۔ کسی بھی چیز سے گزرنے والے کال بیک کو 400 ملتے ہیں اور ٹوکن کا تبادلہ کبھی نہیں ہوتا ہے۔

پھر اس چینل کے لیے ایجنٹ چلائیں جس میں باقاعدہ چیٹ ہو۔

node src/index.js

اس کا آؤٹ پٹ تین غیر واضح پیغامات والے چینل کے لیے چلتا ہے:

[channel-watcher-agent] 3 messages fetched, 3 new.

--- Alex: "Sending draft message" ---
The message "Sending draft message" is noise — it appears to be a test or
accidental send, not a bug report or concrete action item.

...

کوئی اوزار نہیں بلایا گیا اور نہ ہی کوئی مسئلہ اٹھایا گیا۔ منفی مثالیں ان کی نظر سے کہیں زیادہ اہم ہیں۔ لکھنے تک رسائی والے ایجنٹ جو نہیں کہہ سکتے ہیں وہ ذمہ دار ہیں، اور باقاعدہ چیٹ کے علاوہ کچھ بھی سب سے سستا حد ٹیسٹ ہے۔

اب براہ کرم اپنے چینل میں ایک حقیقی بگ رپورٹ پوسٹ کریں۔

ہیلو، /export endpoint 50MB سے بڑی کسی بھی فائل کے لیے ٹائم آؤٹ کا سامنا کر رہا ہے۔ یہ کل کی تعیناتی کے بعد سے مسلسل ہو رہا ہے۔

جب آپ ایجنٹ کو دوبارہ چلاتے ہیں، تو پرانے پیغامات کی اسٹیٹ فائل کے ذریعے دو بار درجہ بندی نہیں کی جائے گی۔

کاپی رائٹ ایک اہم حصہ ہے۔ شناخت کنندہ کے پیچھے GitHub اکاؤنٹ اس صارف کی اپنی OAuth اجازتوں کا استعمال کرتے ہوئے مسئلہ کھولتا ہے، نہ کہ شیئرنگ بوٹ۔ اس شخص کی طرف سے بھی سست جوابات آتے ہیں۔ اگر آپ رسائی منسوخ کرتے ہیں تو اگلی رن ناکام ہو جائے گی۔ getAccessTokenیہ صحیح نتیجہ ہے۔

دوسرے صارف کے لیے تبدیلیاں

شناخت کنندہ کمانڈ لائن پر فراہم کیا جاتا ہے، جو آپ کو پہلے لاگ ان کیے بغیر تنہائی کی جانچ کرنے کی اجازت دیتا ہے۔

node src/index.js                     # the identifier you already authorized
node src/index.js alice@example.com   # a different user entirely

دوسری کمانڈ کسی چینل کو نہیں پڑھتی ہے۔ یہ رک جائے گا:

[slack] no grant for "alice@example.com".
Connect it first: node src/connect.js alice@example.com

رد ہی نقطہ ہے۔ دو حکموں کے درمیان ایجنٹ میں کوئی تبدیلی نہیں ہے۔ ایک جیسے فراہم کنندگان، ٹولز اور کوڈ۔ چونکہ صرف شناخت کنندگان مختلف تھے اور ایلس متفق نہیں تھی، اس لیے ڈکرپٹ کرنے کے لیے کوئی قطاریں نہیں ہیں اور اس سے پہلے کہ آپ Slack کو چھوئیں اس پر عمل درآمد رک جاتا ہے۔

مشترکہ بوٹ ورژن مختلف طریقے سے برتاؤ کرتا ہے۔ دوسری کمانڈ چینل کو پڑھتی ہے اور مسئلے کو بوٹ کو جمع کراتی ہے، کیونکہ اس میں صارف کے لیے مخصوص اجازت شامل نہیں ہوتی ہے۔

اگر ایلس راضی ہوتی ہے تو نیچے کی ہر چیز ایلس کی منظوری کے بعد ہوتی ہے۔ readGrant اس کی قطار لوٹاتا ہے۔ getAccessToken اس کے ٹوکن کو ڈکرپٹ کریں۔ ایک سلیک ریڈ وہ چینلز واپس کرتی ہے جو وہ دیکھ سکتی ہیں، اور اس کا GitHub اکاؤنٹ مسائل لکھتا ہے۔

پیداوار متبادل argv سیشن تلاش کے ذریعے:

const identifier = await lookupIdentifier(session.userId);

اسناد کے بغیر تنہائی کی جانچ

ریپوزٹری میں ایک ٹیسٹ سوٹ ہے جو بدلتا ہے: fetch بلٹ ان Slack اور GitHub اینڈ پوائنٹس کے ساتھ، درخواست کرنا، جوابی تجزیہ کرنا، محفوظ کرنا، اور ریفریش لاجک سبھی OAuth ایپ کو رجسٹر کیے بغیر چلتے ہیں۔

npm test

ان میں سے تین ٹیسٹ نام دینے کے قابل ہیں کیونکہ وہ کوڈ کے اندر کے بجائے اس ٹیوٹوریل کے دعووں کو چیک کرتے ہیں۔

  • سلیک ایکسچینج صارف کے ٹوکنز کو برقرار رکھتا ہے اور بوٹ ٹوکنز کو مسترد کرتا ہے۔ فکسچر دونوں کو لوٹاتا ہے۔ ٹیسٹ کا دعویٰ ہے کہ ذخیرہ شدہ قدر ہے: xoxp- ایک

  • دو صارفین کو ایک ہی ٹول ان پٹ سے دو مختلف ٹوکن ملتے ہیں۔ ایک ہی textایک ہی thread_tsدو شناخت کنندگان، دو مختلف Authorization ہیڈر فراہم کنندہ تک پہنچ جاتا ہے۔

  • منقطع صارفین کے لیے بنائے گئے ٹولز بدلے جانے کے بجائے ناکام ہو جائیں گے۔ اس کا یہ بھی دعویٰ ہے کہ کسی بھی فراہم کنندہ کال کی کوشش نہیں کی گئی، کیونکہ درخواست لیک ہونے کے بعد ناکام ہونا کوئی بڑی ناکامی نہیں ہے۔

پہلے رن پر گزرنے والے ٹیسٹ ناقابل اعتبار ہیں، اس لیے ہم نے جان بوجھ کر جانچنے کے لیے کوڈ توڑ دیا۔ صارف ٹوکن کو بوٹ ٹوکن سے تبدیل کریں اور شناخت کنندہ کو نظر انداز کریں۔ buildToolsاور نمائش identifier ماڈل کی نمائندگی کے اسکیما میں، ہر ایک ایک یا زیادہ ٹیسٹوں میں ناکام ہوتا ہے۔

دوسرے استعمال کے معاملات میں پیٹرن کا اطلاق کیسے کریں۔

پیٹرن کا سلیک کی درجہ بندی سے کوئی تعلق نہیں ہے۔ فارم درج ذیل ہے: ایک ایپ سے پڑھیں، ماڈل کے ساتھ فیصلے کریں، اور دوسری ایپ کو لکھیں، یہ سب ایک صارف کے ذریعے ہے۔

سپلائرز کو تبدیل کرنے کا نتیجہ مختلف مصنوعات میں ہوتا ہے۔

اگلا پڑھیں پر لکھیں نتیجہ
ڈھیلے سے گٹ ہب ٹرائیج چینل کی گفتگو کے ذریعے مسائل کی نشاندہی کریں، جیسا کہ اس ٹیوٹوریل میں ہے۔
Gmail لکیری سپورٹ ای میلز کو ٹریک کردہ کاموں میں تبدیل کریں۔
گوگل کیلنڈر تصور میٹنگ سے پہلے میٹنگ کی تیاری کے نوٹس تیار کیے گئے۔
زینڈیسک سیلز فورس درست اکاؤنٹ کے لیے سپورٹ سگنل کو نوٹ کریں۔
گٹ ہب ڈھیلے سے ان چینلز میں تبدیلیوں کا خلاصہ جن کا آپ خیال رکھتے ہیں۔

ہر قطار ایک ہی تین حصوں کا استعمال کرتی ہے: سپلائر اندراج، شناخت کنندہ کے ذریعہ ٹوکن تلاش، اور ٹول۔ صرف تین چیزیں تبدیل ہوتی ہیں: OAuth ایپ رجسٹریشن، اندرونی API کالز۔ executeاور یہ وہ ان پٹ ہے جو میں نے ماڈل کے لیے لکھا تھا۔

ان پٹ وہ جگہ ہے جہاں پروڈکٹ موجود ہے۔ OAuth پلمبنگ کر رہا ہے۔ یہ فیصلہ کرنا کہ کون سے پیغامات کے مسائل کا امکان ہے اور مسئلہ کو کیا کہنا ہے ایک فیصلہ ہے، اور فیصلہ کئی ہفتوں کا ایک قیمتی حصہ ہے۔

ایک ہی کوڈ تعیناتی کی دونوں شکلوں کو سپورٹ کرتا ہے۔

  • اندرونی ٹیم ایجنٹ: شناخت کنندہ ٹیم کا وہ رکن ہے جس نے عمل درآمد کو متحرک کیا۔ یہ ایک شیڈول یا کمانڈ کے مطابق چلتا ہے۔

  • گاہک کا سامنا کرنے والے ایجنٹس: شناخت کنندگان کرایہ دار اور صارف کے ریکارڈ سے لیے گئے ہیں۔ یہ کسٹمر اکاؤنٹ کے اندر کسٹمر ڈیٹا پر چلتا ہے۔

کوڈ ایک ہی رہتا ہے۔ غلط شناخت کنندہ کے نتائج نہیں ہیں۔

جب میں نے اسے بنایا تو کیا غلط ہوا؟

یہ وہ مسائل ہیں جن کا مجھے پروجیکٹ کی تعمیر کے دوران سامنا کرنا پڑا۔ یہ تقریباً اس ترتیب میں ہے جس طرح وہ ظاہر ہوتے ہیں۔ اگر آپ کو ایک ہی مسئلہ درپیش ہے، تو عام طور پر کم اصلاحات ہوتی ہیں۔

سلیک ری ڈائریکٹ یو آر ایل کو اسٹور نہیں کرتا ہے۔

کوڈ پر عمل درآمد سے پہلے علامت ظاہر ہوتی ہے۔ سلیک ایپ کنفیگریشن پیج پر قبولیت کو مسترد کریں۔ http://localhost:3000/callback.

سلیک کو ری ڈائریکٹ URLs میں HTTPS کی ضرورت ہے بغیر کسی استثناء کے: localhost. مقامی سرٹیفکیٹ جاری کریں۔ mkcertرجسٹریشن https:// شکلیں اور پوائنٹس TLS_CERT_PATH اور TLS_KEY_PATH فائل میں لکھا تھا۔ GitHub کسی بھی اسکیم کو قبول کرتا ہے، لہذا ایک ہی HTTPS URL دونوں ایپس کے لیے کام کرے گا۔

آپ کا براؤزر آپ کو خبردار کرے گا کہ سرٹیفکیٹ قابل بھروسہ نہیں ہے۔

mkcert -install یہ ایک ایسا مرحلہ ہے جو mkcert کی مقامی اجازت کو سسٹم ٹرسٹ اسٹور میں شامل کرتا ہے، اور اگر آپ اسے چھوڑ دیتے ہیں تو آپ کو ایک سرٹیفکیٹ چھوڑ دیا جائے گا جسے براؤزر تسلیم نہیں کرتا ہے۔

اسے ایک بار چلانے سے مستقبل کے تمام سرٹیفکیٹ mkcert کے مسائل حل ہو جائیں گے۔ ایک خود دستخط شدہ سرٹیفکیٹ کے ساتھ بنایا گیا ہے۔ openssl ہمیشہ چوکس رہیں کیونکہ کوئی بھی اس پر بھروسہ نہیں کرتا۔

ری ڈائریکٹ URI مماثل نہیں ہے۔

دونوں فراہم کنندگان redirect_uri اگر آپ اسے ایپ میں رجسٹرڈ کے مقابلے بھیجیں گے تو موازنہ درست ہوگا۔ ٹریلنگ سلیش، 127.0.0.1 اس کے بجائے localhost، http جہاں آپ نے اندراج کیا ہے۔ httpsیا دیگر تمام بندرگاہیں ناکام ہوجاتی ہیں۔

غلطی رضامندی سے پہلے فراہم کنندہ کے اپنے صفحے پر پہنچ جاتی ہے، اس لیے کم از کم اس کی نشاندہی کرنا آسان ہے۔ برقرار رکھنا OAUTH_REDIRECT_URI اسے سچائی کے واحد ذریعہ کے طور پر استعمال کریں اور اسے فراہم کنندہ کی رجسٹری کی طرح اجازت کے URL اور ٹوکن ایکسچینج دونوں کو منتقل کریں۔

کال بیک پورٹ پہلے ہی استعمال میں ہے۔

connect.js بندرگاہ کو باندھنا۔ OAUTH_REDIRECT_URIپورٹ 3000 بڑے پیمانے پر استعمال کیا جاتا ہے۔ غیر عمل شدہ EADDRINUSE یہ ایک اسٹیک ٹریس تیار کرتا ہے جو OAuth کے بارے میں کچھ نہیں کہتا ہے، لہذا پروجیکٹ اسے پکڑتا ہے اور اسے بتاتا ہے کہ اس کے بجائے کیا کرنا ہے۔

بندرگاہ کو تبدیل کرنے کا مطلب ہے بندرگاہ کو تین جگہوں پر تبدیل کرنا: .envSlack ایپ کے لیے URL اور GitHub ایپ کے لیے کال بیک URL کو ری ڈائریکٹ کریں۔ اگر کوئی غائب ہے تو آپ کو پچھلی غلطی مل جائے گی۔

صحت کی جانچ جائز کال بیک کو مسترد کرتی ہے۔

ریاستی اقدار میموری میں رہتی ہیں اور استعمال ہونے پر حذف ہوجاتی ہیں۔ دوبارہ شروع ہو رہا ہے۔ connect.js لنک کو کھولنے یا کال بیک ٹیب کو ریفریش کرنے کے بعد، دونوں ایک ایسی حالت بناتے ہیں جو اب نقشے میں نہیں ہے۔

دونوں صحیح رد ہیں۔ ایک نیا لنک بنائیں اور دوبارہ شروع کریں۔

ٹول کال اجازت کی غلطی یا خالی نتیجہ لوٹاتا ہے۔

دونوں لاپتہ دائرہ کار یا منسوخ شدہ گرانٹس کی وجہ سے ہوتے ہیں۔

GitHub جوابات 403 اگر آپ کی گرانٹ ناکافی ہے تو "آپ کو وسائل تک رسائی نہیں ہے"۔ repo. سست جوابات 200 کے ساتھ ok: false اور درج ذیل غلطی missing_scope. دائرہ کار کی فہرست میں ترمیم کریں اور پھر اسے رضامندی کے ذریعے صارف کو واپس بھیجیں کیونکہ موجودہ گرانٹس کو سابقہ ​​طور پر دائرہ کار نہیں دیا جائے گا۔

جزوی دائرہ کار گرانٹ کنکشن کے وقت کی بجائے استعمال کے وقت ناکام ہو جاتا ہے۔یہی وجہ ہے کہ علامات پراسرار لگتی ہیں۔ رضامندی کی سکرین کامیاب ہو گئی، ٹوکن بالکل ٹھیک محفوظ ہو گیا، اور چند گھنٹوں بعد ٹول کال کے دوران ناکامی واقع ہو گئی۔

ایجنٹ ایسے چینلز پڑھ رہے ہیں جنہیں نہیں پڑھنا چاہیے۔

واحد سب سے زیادہ ممکنہ وجہ اسٹوریج ہے۔ json.access_token اس کے بجائے json.authed_user.access_token سلیک ایکسچینج۔ دونوں تار ہیں، دونوں سچائیاں ہیں، اور دونوں کام کرتے ہیں (ایک ایپ کے ساتھ کام کرتا ہے، شخص کے ساتھ نہیں)۔

جو واپس آتا ہے اس کی حد بتاؤ۔ صارف کا ٹوکن صرف اس شخص کا چینل واپس کرتا ہے۔ اگر conversations.history وہ چینل لوٹاتا ہے جس میں موجودہ صارف نے کبھی حصہ نہیں لیا اور بوٹ ٹوکن اسٹور میں موجود ہے۔

ٹول کال کو غلط صارف کے طور پر انجام دیا جاتا ہے۔

ٹوکن یا شناخت کنندہ کو پاس کرنا جو کسی اور سے تعلق رکھتا ہے غلط کام کرتا ہے۔

دو عادات اس کو روکتی ہیں: کلائنٹ ان پٹ کے بجائے کالر کی تصدیق کرنے کے بعد سرور سائیڈ شناخت کنندگان کی تصدیق کریں۔ پھر ہم شناخت کنندہ کو بند ہونے کی دلیل کے طور پر استعمال کرتے ہیں۔ buildTools اور ہر ایک execute اسے اپنا ٹوکن مل جاتا ہے، لہذا کوئی کوڈ پاتھ آوارہ اسناد کے ساتھ نہیں گزر سکتا۔

ریفریش ایک بار کام کرتا ہے اور پھر رک جاتا ہے۔

ریفریش ٹوکنز کو گھمایا جاتا ہے۔ ایک ریفریش جو ایک نیا رسائی ٹوکن دوبارہ بناتا ہے لیکن پرانے ریفریش ٹوکن کو برقرار رکھتا ہے فوری طور پر کامیاب ہو جاتا ہے اور اگلے سائیکل پر ناکام ہو جاتا ہے، بگ اور علامات کے درمیان 12 گھنٹے ہوتے ہیں۔

saveGrant اس وجہ سے، ہم ایک مکمل اہل ٹوکن آبجیکٹ استعمال کرتے ہیں۔ ریفریش سے لوٹی ہوئی ہر چیز کو دوبارہ لکھتا ہے۔

ایجنٹ فائل ڈپلیکیشن کا مسئلہ

دو وجوہات۔ لاپتہ یا غیر لاگ شدہ اسٹیٹ فائلوں کی وجہ سے تمام رنز کی دوبارہ درجہ بندی کی جائے گی۔ یا stopWhen الجھے ہوئے ماڈلز کو پہلے ہی کامیاب ہونے والے ٹولز کو دوبارہ آزمانے کی اجازت دینے کے لیے کافی راؤنڈز کی اجازت دیں۔

پہلے اسٹیٹس فائل کو چیک کریں۔ پھر یقینی بنائیں کہ ٹول کی واپسی کی قدر واضح طور پر کامیابی کی نشاندہی کرتی ہے۔ اس کی وجہ یہ ہے کہ ایک مبہم نتیجہ کے لیے دوبارہ کوشش کی ضرورت ہے۔

نتیجہ

ہم نے ایک ایجنٹ بنایا ہے جو سلیک چینلز کو پڑھتا ہے، اس بات کا تعین کرتا ہے کہ کون سے پیغامات اصل کارروائی کو بیان کرتے ہیں، اس کے لیے ایک GitHub مسئلہ ترتیب دیتا ہے، اور تھریڈ کے جواب کے ساتھ لوپ کو بند کرتا ہے۔ تمام کالز مخصوص صارف کی اپنی OAuth اجازتوں کے ساتھ OAuth فلو اور ایک ٹوکن اسٹور کے ذریعے کی گئی تھیں جسے ہم نے خود لکھا تھا۔

پانچ خیالات تمام شراکت داروں سے خطاب کر رہے ہیں.

  • ٹولز API کالز اور ماڈلز کی تفصیل ہیں۔وضاحت زیادہ تر کام ہے۔

  • شناخت کنندہ ایجنٹ کوڈ میں ٹوکن کی جگہ لے لیتا ہے۔ مندرجہ بالا سبھی ایک چھوٹے فنکشن میں صارف کا حوالہ ہینڈل کرتا ہے نہ کہ اسناد کو، لہذا ٹوکن کبھی بھی ماڈل ان پٹ یا لاگز تک نہیں پہنچتا ہے۔

  • کنکشن کا وقت اور رن ٹائم الگ الگ بہاؤ ہیں۔ رضامندی فی صارف، فی ایپ ایک بار ہوتی ہے۔ رن ٹائم شناخت کنندہ کو چیک کرتا ہے اور سستی سے ٹوکن حاصل کرتا ہے۔

  • منظوری آپ کی ہے۔ ٹوکن اسٹور جواب دیتا ہے کہ کون سا ٹوکن شناخت کنندہ کا ہے۔ آیا کال کرنے والا اس شناخت کنندہ کے ساتھ کام کر سکتا ہے یہ ایک سوال ہے جس کا جواب صرف کوڈ میں دیا جا سکتا ہے۔

  • ملٹی پرووائیڈر سپورٹ رجسٹری کا مسئلہ ہے، فن تعمیر کا مسئلہ نہیں۔ایک بار جب IDs ایک سٹرنگ میں ہوں۔

وہ تفصیلات جو سب سے زیادہ وزن رکھتی ہیں وہ بھی سب سے چھوٹی ہیں۔ authed_user.access_token بلکہ access_token. ایک انتساب تک رسائی اس بات کا تعین کرتی ہے کہ آیا ایجنٹ ورک اسپیس میں پہلے سے موجود اجازتوں کا احترام کرتا ہے یا خاموشی سے انہیں نظرانداز کرتا ہے۔

یہ وہ جگہ ہے جہاں آپ اپنی شکل رکھتے ہیں اور سپلائرز کو تبدیل کرتے ہیں۔ پڑھنے کا آدھا پوائنٹ جی میل کی طرف، آدھا لکھنا لکیری کی طرف اشارہ کرتا ہے، اور پھر ہم ان پٹ کو ماڈل پر دوبارہ لکھتے ہیں۔ شناختی پلمبنگ تبدیل نہیں ہوتی ہے۔

مکمل ماخذ github.com/saif-shines/channel-watcher-agent پر ہے۔

یہ مضمون جو کچھ ہم نے سیکھا ہے اس کی تشکیل نو کرتا ہے۔ پیمانے کی کٹیہ ٹوکن اسٹور کا میزبان ورژن ہے جسے ہم نے ابھی بنایا ہے۔

اوپر تک سکرول کریں۔