SSD Nodes Learn 🎉 VPS $5.50/माह से
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-21

DeepSeek Harness के लिए अपना dsh प्लगइन कैसे बनाएं

एक खाली फोल्डर से dsh प्लगइन बनाने का तरीका जानें। इसमें package.json के जरूरी फील्ड्स, माउंटिंग पैच फाइल, एक वास्तविक टूल और दो मुख्य हुक्स का विवरण दिया गया है।

dsh प्लगइन वास्तव में क्या है

dsh प्लगइन एक npm पैकेज है जो एक apply फंक्शन को एक्सपोर्ट करता है और एक छोटी YAML फाइल के साथ आता है जो DeepSeek Harness को इसे लोड करने का निर्देश देती है। इसे सीखने के लिए कोई अलग प्लगइन SDK नहीं है। dsh एक Cordis एप्लिकेशन है, और "सब कुछ एक प्लगइन है" का अर्थ शाब्दिक है: टूल रजिस्ट्री, एजेंट लूप, सेशन स्टोर और वेब सर्वर, ये सभी उसी प्लगइन ट्री की पंक्तियाँ हैं जिसमें आपका पैकेज शामिल होता है।

Cordis एक सामान्य कंपोजिशन फ्रेमवर्क है, जिसे स्वतंत्र रूप से बनाया गया है और वर्षों से Koishi चैटबॉट फ्रेमवर्क के आधार के रूप में उपयोग किया जा रहा है। यह लोडिंग और अनलोडिंग को संभालता है, और यह प्लगइन्स के बीच निर्भरता (dependencies) को हल करता है। यह एजेंटों के बारे में कुछ नहीं जानता है। एजेंट जैसा सब कुछ इसके ऊपर स्थित हार्नेस पैकेजों से आता है, यही कारण है कि नीचे दिया गया प्लगइन का स्वरूप इतना छोटा दिखता है। आपको जो कुछ भी मिलता है, उसका अधिकांश हिस्सा इनहेरिट किया हुआ होता है।

एक प्लगइन के दो भाग होते हैं। होस्ट भाग Node में चलता है, टूल और इवेंट लिसनर को रजिस्टर करता है, और अपनी स्वयं की सेवाएं प्रदान कर सकता है। ब्राउज़र भाग Web UI के अंदर चलता है और इंटरफ़ेस स्लॉट को रजिस्टर करता है। पहला प्लगइन लगभग हमेशा केवल होस्ट वाला होता है, इसलिए ब्राउज़र भाग को तब तक वैकल्पिक मानें जब तक आपको इसकी आवश्यकता न हो।

यह गाइड @deepseek-ai/dsh वर्ज़न 0.1.0-rc.7 के लिए लिखी गई है, जो 19 अगस्त 2026 को npm latest टैग पर उपलब्ध था। dsh एक डेवलपर प्रीव्यू है और इसके स्वयं के README में कहा गया है कि इसमें संगतता (compatibility) को तोड़ने वाले बदलाव होंगे। नीचे दिए गए प्रत्येक की-नाम (key name) को उस तारीख पर अपस्ट्रीम डॉक्यूमेंटेशन और रिपॉजिटरी से पढ़ा गया था। किसी पर भी निर्भर होने से पहले उन्हें दोबारा पढ़ें, क्योंकि एक प्रीव्यू API रिलीज कैंडिडेट्स के बीच फील्ड्स के नाम बदल देता है। यदि हार्नेस अभी तक नहीं चल रहा है, तो इसे पहले VPS पर DeepSeek Harness और dsh API की और मॉडल कॉन्फ़िगरेशन के साथ सेटअप करें, फिर यहाँ वापस आएं।

किसी भी चीज़ को पैकेज करने से पहले एक स्क्रैच फ़ाइल लोड करें

पहले पैकेजिंग करना इसे सीखने का धीमा तरीका है। एक सिंगल फ़ाइल लोड करें, यह साबित करें कि रनटाइम आपके कोड को कॉल करता है, और फिर उसे पैकेज करें।

हैरनेस चेकआउट के बाहर एक फ़ोल्डर बनाएँ और उसमें एक फ़ाइल रखें।

import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  console.log('[hello-plugin] plugin loaded')
}

export const name वह मेटाडेटा है जिसका उपयोग डायग्नोस्टिक्स में प्लगइन को लेबल करने के लिए किया जाता है। apply पूरा कॉन्ट्रैक्ट है: Cordis इसे एक बार कॉल करता है और आपके प्लगइन के लिए स्कोप किया गया एक कॉन्टेक्स्ट पास करता है। जब प्लगइन डिस्पोज़ किया जाता है, तो उस कॉन्टेक्स्ट पर आपके द्वारा रजिस्टर की गई कोई भी चीज़ अपने आप अनडू (undo) हो जाती है।

इसके बगल में, cordis.yml लिखें।

- insert:
    - id: hello
      name: '/absolute/path/to/scratch-plugin/hello.ts'

अब उस फ़ाइल को ऊपर लेयर करके एक प्रोफ़ाइल बूट करें।

dsh web --patch ./scratch-plugin/cordis.yml

यदि dsh आपके PATH पर नहीं है, तो npx @deepseek-ai/dsh web --patch ./scratch-plugin/cordis.yml वही काम करता है। वह npx रूट आपको इस गाइड में वर्णित वर्शन के बजाय एक पुराना कैश किया हुआ रिलीज़ कैंडिडेट दे सकता है, इसलिए यदि हैरनेस किसी डॉक्यूमेंटेड फ्लैग को पूरी तरह से अस्वीकार कर देता है, तो शुरू करने से पहले dsh इंस्टॉलेशन और वर्शन त्रुटियों के समाधान को देखें। आपको dsh शुरू करने वाले टर्मिनल में [hello-plugin] plugin loaded दिखाई देना चाहिए। यदि कुछ भी दिखाई नहीं देता है, तो रो (row) रिज़ॉल्व नहीं हुई है।

name फ़ील्ड एक npm पैकेज नाम या फ़ाइलसिस्टम पाथ लेती है, और अपस्ट्रीम डॉक्यूमेंटेशन बताती है कि पाथ एब्सोल्यूट होना चाहिए। जब कोई स्क्रैच प्लगइन आउटपुट नहीं देता है, तो सबसे पहले एक रिलेटिव ./hello.ts की जाँच करनी चाहिए। दूसरी चीज़ फ़ाइल एक्सटेंशन है। डॉक्यूमेंटेड लूप को हैरनेस रिपॉजिटरी के क्लोन से pnpm dsh web --patch ... के रूप में चलाया जाता है, जहाँ TypeScript एंट्रीज़ tsx के माध्यम से लोड होती हैं। यदि आपका dsh npm से आया है, तो रो को प्लेन JavaScript पर पॉइंट करें, या पहले फ़ाइल को बिल्ड करें।

--patch एक लॉन्चर फ्लैग है और इसका ओवरले सबसे अंत में, हर बंडल और आपकी अपनी प्रोफ़ाइल पैच के बाद लागू किया जाता है। इसलिए एक स्क्रैच ओवरले हमेशा जीतता है, जो कि इटरेशन के दौरान आप बिल्कुल यही चाहते हैं।

सबसे छोटा टूल लिखें जो कुछ उपयोगी काम करे

एक लॉग लाइन यह साबित करती है कि प्लगइन लोड हो गया है। एक टूल यह साबित करता है कि प्लगइन एजेंट का हिस्सा है।

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'greet-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'greet',
    description: 'Greet someone by name.',
    parameters: {
      name: { type: 'string', required: true, description: 'The name to greet' },
    },
    output: {
      schema: { type: 'string' },
      render: (_args, value) => [{ type: 'text', text: value }],
    },
    async execute(args) {
      return `Hello, ${args.name}!`
    },
  }))
}

export const inject = ['tools'] वह लाइन है जिसे लोग छोड़ देते हैं। Cordis कॉन्फ़िगरेशन में प्रविष्टियाँ एक साथ शुरू होती हैं, इसलिए फ़ाइल में किसी पंक्ति की स्थिति लोड होने के क्रम के बारे में कोई गारंटी नहीं देती है। क्रमबद्धता घोषित निर्भरताओं (declared dependencies) से आती है। inject, Cordis को यह बताता है कि आपके apply को कॉल करने से पहले वह ctx.tools के मौजूद होने तक प्रतीक्षा करे, और इसके बिना आपका कोड ऐसे समय पर चल सकता है जब रजिस्टर करने के लिए रजिस्ट्री वहां मौजूद न हो।

ऑब्जेक्ट का बाकी हिस्सा वह अनुबंध (contract) है जिसे मॉडल देखता है। parameters तर्क स्कीमा (argument schema) है, और execute उन तर्कों को प्राप्त करता है जिन्हें पहले ही इसके विरुद्ध पार्स किया जा चुका है। output.schema उस मान का वर्णन करता है जिसे execute लौटाता है, जबकि render उस मान को उन कंटेंट ब्लॉक्स में परिवर्तित करता है जिन्हें मॉडल पढ़ता है। उन दोनों को अलग रखने से ही इंटरफ़ेस एक चीज़ दिखा पाता है जबकि मॉडल कुछ और पढ़ता है।

प्रोफ़ाइल शुरू करें और असिस्टेंट से किसी का नाम लेकर अभिवादन करने के लिए कहें। उत्तर आपके execute के माध्यम से वापस आता है। ctx के माध्यम से पंजीकरण प्रतिवर्ती (reversible) है, इसलिए प्लगइन को डिस्पोज़ करने से आपके लिए टूल का पंजीकरण रद्द हो जाता है। ऐसी किसी भी चीज़ के लिए जिसके बारे में Cordis नहीं जान सकता, जैसे कि सॉकेट या फ़ाइल हैंडल, ctx.effect() को कॉल करें और उसे एक डिस्पोज़र (disposer) दें।

दो एक्सटेंशन पॉइंट जिनसे पहला प्लगइन वास्तव में जुड़ता है

सीम्स (seams) की पूरी सूची लंबी है। इनमें से दो लगभग हर पहले प्लगइन को कवर करते हैं।

Conversation events एक टिकाऊ, लॉग की गई स्ट्रीम हैं। इनके नाम session/event, turn/start, turn/end, step/start, step/end, user/message, assistant/message, assistant/chunk, tool/call और tool/result हैं। आप इनसे एक सामान्य लिसनर जोड़ते हैं।

ctx.on('tool/call', (payload) => {
  console.log('[my-plugin] tool/call', JSON.stringify(payload))
})

पेलोड को एक बार प्रिंट करें और उसे पढ़ें। किसी भी गाइड से, जिसमें यह भी शामिल है, पेलोड फ़ील्ड के नाम कॉपी न करें, क्योंकि पेलोड का आकार प्रीव्यू API का वह हिस्सा है जो सबसे अधिक बदलता है।

दूसरा एक्सटेंशन पॉइंट वॉटरफॉल है। agent/pre-step, agent/request, agent/request-error, llm/stream और tools/* इवेंट्स वॉटरफॉल हैं, और एक वॉटरफॉल लिसनर का सिग्नेचर अलग होता है। यह एक next कॉलबैक लेता है, और चेन तभी आगे बढ़ती है जब इसे कॉल किया जाता है।

ctx.on('agent/request', async (payload, next) => {
  const startedAt = Date.now()
  const downstream = await next()
  console.log('[my-plugin] model request took', Date.now() - startedAt, 'ms')
  return downstream
})

यदि आप await next() को भूल जाते हैं, तो आपने कोई हुक नहीं जोड़ा है। आपने मॉडल कॉल को कुछ भी नहीं से बदल दिया है, और एजेंट वहीं रुक जाता है, क्योंकि शॉर्ट सर्किट करना उस गेटवे प्लगइन के लिए डिज़ाइन किया गया व्यवहार है जो जानबूझकर किसी अनुरोध को अस्वीकार करता है। यही एक अंतर पहले प्लगइन के साथ अधिकांश भ्रम पैदा करता है। next() कॉल को उसके आसपास कुछ भी लिखने से पहले लिखें।

agent/request स्वयं मॉडल कॉल को रैप करता है। इसका पेलोड कॉल करने वाले एजेंट, ओपन टर्न नंबर, वह स्टेप जिससे अनुरोध संबंधित है और उस टर्न का एबॉर्ट सिग्नल ले जाता है, जो इसे रिक्वेस्ट लॉगर या रेट लिमिटर के लिए सही सीम बनाता है। tools/* वॉटरफॉल एक लेयर नीचे समान आकार के होते हैं। tools/pre-execute डिस्पैच से पहले अनुमति देता है, अस्वीकार करता है या अनुमोदन का अनुरोध करता है। tools/execute डिस्पैच को रैप करता है। tools/post-execute सामान्यीकृत परिणाम को बदल या ब्लॉक कर सकता है। tools/result केवल अंतिम परिणाम का अवलोकन करता है।

इसे एक ऐसे बंडल के रूप में पैक करें जिसे अन्य लोग इंस्टॉल कर सकें

एक बंडल एक npm पैकेज होता है जिसका package.json एक dsh.bundle फ़ील्ड घोषित करता है जो इसकी पैच फ़ाइल की ओर इशारा करता है। वह घोषणा ही एक स्क्रैच फ़ाइल और इंस्टॉल करने योग्य चीज़ के बीच का एकमात्र अंतर है।

{
  "name": "dsh-plugin-hello",
  "version": "0.1.0",
  "type": "module",
  "main": "lib/index.js",
  "files": ["lib", "cordis.patch.yml", "README.md", "LICENSE"],
  "engines": { "node": "^22.19 || >=24", "dsh": ">=0.1.0-rc.6" },
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },
  "keywords": ["dsh-plugin", "deepseek-harness"],
  "scripts": { "build": "tsdown", "prepare": "pnpm run build" },
  "exports": {
    ".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" },
    "./cordis.patch.yml": "./cordis.patch.yml",
    "./package.json": "./package.json"
  }
}

इसके बगल में स्थित cordis.patch.yml छोटा होता है।

- insert:
    - id: dsh-plugin-hello
      name: dsh-plugin-hello

पंक्ति name पैकेज का नाम है, इसलिए उन दोनों स्ट्रिंग्स का मेल खाना आवश्यक है। पंक्ति id वह है जिसे बाद की लेयर तब लक्षित करती है जब कोई उपयोगकर्ता आपके कॉन्फ़िगरेशन को ओवरराइड करता है, इसलिए कुछ स्थिर चुनें और इसे कभी भी किसी अलग प्लगइन के लिए दोबारा उपयोग न करें।

files में cordis.patch.yml को सूचीबद्ध करना अनिवार्य है। यदि आप इसे छोड़ देते हैं, तो प्रकाशित टारबॉल में एक dsh.bundle.patch होगा जो ऐसी फ़ाइल की ओर इशारा करेगा जिसे कभी पैक ही नहीं किया गया था, इसलिए पैकेज इंस्टॉल तो हो जाएगा लेकिन ट्री में कुछ भी योगदान नहीं देगा।

इसे अपने प्लगइन फ़ोल्डर वाली निर्देशिका से एक प्रोफ़ाइल में इंस्टॉल करें।

dsh plugin --profile demo add ./dsh-plugin-hello
dsh --profile demo --dump-config
dsh --profile demo

dsh plugin --profile <name> अपने बाकी तर्कों को उस प्रोफ़ाइल निर्देशिका के भीतर pnpm को भेजता है, इसलिए add और remove उसी तरह व्यवहार करते हैं जैसे pnpm करता है। dsh plugin --profile demo remove dsh-plugin-hello के साथ अनइंस्टॉल करें। web और headless प्रोफ़ाइल पहली बार उपयोग किए जाने पर शिप किए गए टेम्प्लेट से खुद को बना लेती हैं, और किसी भी अन्य प्रोफ़ाइल नाम को dsh plugin के माध्यम से बनाया जाना चाहिए।

आपकी row composed tree से क्यों गायब है

Composition एक खाली entry list से शुरू होती है और layers को एक निश्चित क्रम में stack करती है। सबसे पहले profile के dsh.profile.bundles में नामित प्रत्येक bundle, उसी क्रम में जिस क्रम में वे सूचीबद्ध हैं। फिर profile की अपनी cordis.patch.yml। उसके बाद $DSH_HOME/cordis.patch.yml। अंत में command line से कोई भी --patch overlay। बाद की layers id के आधार पर पिछली rows को replace कर देती हैं।

Profiles $DSH_HOME/profiles/<name> के अंतर्गत रहती हैं। एक profile directory में एक package.json होता है जिसमें dsh.profile manifest और उसकी क्रमित bundles list होती है, साथ ही उपयोगकर्ता की अपनी patch file भी होती है। Bundle के नाम पहले dsh installation से और फिर profile के node_modules से resolve होते हैं, जहाँ pnpm एक out of tree plugin रखता है।

dsh --profile demo --dump-config बिना कुछ boot किए पूरी तरह से composed tree को print करता है, और वह output debugging के लिए विभाजक रेखा है। यदि आपकी row id अनुपस्थित है, तो समस्या composition की है: एक ऐसा नाम जो resolve नहीं होता, या एक patch file जिसे कभी pack नहीं किया गया। यदि row मौजूद है और कुछ नहीं होता है, तो समस्या आपके code में है। पहले उस प्रश्न का उत्तर दें और आप अधिकांश अनुमान लगाने से बच जाएंगे।

जहाँ लोडिंग त्रुटियाँ वास्तव में दिखाई देती हैं

apply के भीतर आने वाली त्रुटि स्पष्ट होती है। प्रक्रिया उस अपवाद (exception) के साथ समाप्त हो जाती है और आपको एक स्टैक ट्रेस मिलता है जो आपकी अपनी लाइन की ओर इशारा करता है।

रिज़ॉल्यूशन विफलताएँ शांत होती हैं। लोडर क्रैश होने के बजाय Cordis लॉगर के माध्यम से उस मॉड्यूल की रिपोर्ट करता है जिसे वह रिज़ॉल्व नहीं कर सकता। अपस्ट्रीम ट्यूटोरियल चेतावनी देता है कि ये संदेश स्टार्टअप के समय खोए हुए लग सकते हैं, क्योंकि ये कंसोल एक्सपोर्टर के अटैच होने से पहले ही उत्सर्जित हो जाते हैं। इसलिए, पाथ में टाइपिंग की गलती बिल्कुल ऐसी दिखती है जैसे कोई प्लगइन लोड तो हुआ लेकिन उसने कुछ नहीं किया। यही कारण है कि किसी भी कोड को पढ़ने से पहले ऊपर दिए गए --dump-config चेक को चलाना सार्थक है।

विकास के दौरान apply में पहली स्टेटमेंट के रूप में console.log को रखें। इसकी अनुपस्थिति आपको यह बताती है कि समस्या का कौन सा हिस्सा आपके सामने है, और बाद में इसे हटाने में कोई लागत नहीं आती। सर्वर पर, पुनरावृत्ति (iterate) करते समय हार्नेस को सर्विस मैनेजर के बजाय फोरग्राउंड में चलाएँ, ताकि लोडर का आउटपुट आपके टर्मिनल तक पहुँचे, न कि किसी ऐसे जर्नल में जिसे आपको जाकर पढ़ना पड़े।

बिना पूरी दुनिया को रीस्टार्ट किए बदलाव करना

आज के समय में होस्ट के लिए सबसे ईमानदार जवाब यही है कि आप रीस्टार्ट करें। वेब एप्लिकेशन बंडल अपने shared hot module reload row के साथ डिसेबल होकर आता है, और फाइल में एक नोट है कि रीलोड लाइफसाइकिल का परीक्षण होने के बाद इसे फिर से इनेबल कर दिया जाएगा। क्लाइंट साइड रीलोड चेन हमेशा माउंटेड रहती है, लेकिन जब तक rebuild watcher क्लाइंट बंडल्स को फिर से नहीं लिखता, तब तक यह निष्क्रिय रहती है, इसलिए यह आपके Node वाले हिस्से के लिए भी कुछ नहीं करती।

जो रीलोड अभी मौजूद ही नहीं है, उसके पीछे भागने के बजाय रीस्टार्ट को सस्ता बनाएं। प्लगइन को एक ही फाइल में रखें। इसे किसी प्रोफाइल में इंस्टॉल करने के बजाय --patch के साथ लोड करें, ताकि एडिट और रन के बीच कोई build step या pnpm step न आए। हर चीज को ctx के माध्यम से रजिस्टर करें ताकि रीस्टार्ट के बाद कोई डुप्लिकेट टूल या पुराना लिसनर न रह जाए। जो कुछ भी आप खुद एलोकेट करते हैं, उसे एक वास्तविक disposer के साथ ctx.effect() में रैप करें, क्योंकि disposer न होने का सामान्य लक्षण यह है कि दूसरा रन उस पोर्ट पर फेल हो जाता है जिसे पहले रन ने अभी भी होल्ड कर रखा है।

यदि आप अपने लैपटॉप के बजाय सर्वर पर चल रहे हार्नेस के खिलाफ डेवलप करते हैं, तो ऊपर दी गई बातों में कोई बदलाव नहीं आता, लेकिन Web UI बाइंडिंग मायने रखती है। पोर्ट 3080 पर लूपबैक बाइंड बताता है कि पेज अपने आप क्यों नहीं खुलता और इसके बारे में क्या करना चाहिए।

ब्राउज़र वाला हिस्सा और उस पर कितना भरोसा करें

इसे केवल तभी जोड़ें जब आपके प्लगइन को अपने स्वयं के इंटरफ़ेस की आवश्यकता हो। इसे बंडल के समान dsh फ़ील्ड में घोषित किया जाता है।

{
  "dsh": {
    "client": {
      "platform": "web",
      "inject": [],
      "external": [],
      "immediately": false
    }
  },
  "exports": {
    ".": "./src/index.ts",
    "./client": "./src/client/apply.ts",
    "./package.json": "./package.json"
  }
}

"platform": "web" आवश्यक है, और यदि पैकेज में कोई ./client एक्सपोर्ट नहीं है तो स्कैनर त्रुटि देता है, इसलिए एक्सपोर्ट मैप एक सुविधा के बजाय मैनिफ़ेस्ट का हिस्सा है। क्लाइंट एंट्री को क्लाइंट रनटाइम प्रकार के साथ विस्तृत Cordis Context प्राप्त होता है, और प्रत्येक पंजीकरण ctx.slots.register के माध्यम से apply के अंदर होता है। वहां मॉड्यूल स्तर के साइड इफेक्ट्स की अनुमति नहीं है।

import type { Context } from 'cordis'
import type { DshClientContext } from '@deepseek-ai/dsh-client-runtime'

export async function apply(ctx: Context & DshClientContext) {
  ctx.slots.register({ name: 'domain.entry.slot' }, MyComponent)
}

शुरू करने से पहले दो विवरण जानना उपयोगी है। क्लाइंट मैनिफ़ेस्ट में inject शेड्यूलिंग के बजाय दस्तावेज़ीकरण है: यह पैकेज स्तर की निर्भरता किनारों (dependency edges) को रिकॉर्ड करता है और सक्रियण क्रम को नियंत्रित नहीं करता है। external वह जगह है जहाँ आप बेसलाइन के बाहर मॉड्यूल अनुरोधों को घोषित करते हैं, ताकि आपके प्लगइन द्वारा उनके मांगे जाने से पहले ही वे तैयार हो जाएं। यह प्रीव्यू का सबसे तेज़ी से बदलने वाला हिस्सा है, इसलिए कोड लिखते समय उस दिन हार्नेस रिपॉजिटरी में packages/client/AGENTS.md पढ़ें, न कि उस दिन जब आप इसके बारे में कोई गाइड पढ़ रहे हों।

Publish करें, और बताएं कि आपका प्लगइन किन चीजों को एक्सेस करता है

GitHub रिपॉजिटरी में dsh-plugin टॉपिक जोड़ने से वह उस सूची में आ जाता है जिसे लोग प्लगइन्स खोजते समय देखते हैं। यह किसी अनजान व्यक्ति के भरोसे पर किया गया दावा है, और इसकी अपनी जिम्मेदारियां हैं। ये जिम्मेदारियां उस dsh प्लगइन को इंस्टॉल करने से पहले उसकी जांच करने के लिए गाइड का सटीक उल्टा हैं, जिसे हम पाठकों को चेक करने के लिए कहते हैं। इसलिए, उस चेकलिस्ट के अनुसार लिखना इसे पास करने का सबसे आसान तरीका है।

  • अपनी dependencies को पिन करें। transitive dependency पर caret range का उपयोग करने का मतलब है कि जो पैकेज पिछले हफ्ते सुरक्षित था, वह इस हफ्ते अलग कोड चला सकता है। यही वह सटीक तरीका है जिसके जरिए सर्वर पर npm सप्लाई चेन हमले होते हैं।
  • मैनिफेस्ट में स्पष्ट करें कि आप किन चीजों को एक्सेस करते हैं। आपकी inject सूची इस बात का एक ईमानदार और मशीन-पठनीय सारांश है कि आप किन हार्नेस सेवाओं का उपयोग करते हैं। एक समीक्षक इसे कुछ ही सेकंड में पढ़ लेता है और इसके आधार पर अपनी राय बना लेता है।
  • कोई भी साइलेंट नेटवर्क कॉल न करें। यदि कोई टूल API को कॉल करता है, तो README में होस्ट का नाम लिखें और endpoint को कॉन्फ़िगर करने योग्य बनाएं। जो प्लगइन किसी ऐसे सर्वर से संपर्क करता है जिसका उसने कभी उल्लेख नहीं किया, उसे उन लोगों द्वारा हटा दिया जाएगा जो इन चीजों का ऑडिट करते हैं।
  • files को सीमित रखें। पूरे वर्किंग फोल्डर को पब्लिश करने से ही कोई गोपनीय क्रेडेंशियल फाइल रजिस्ट्री तक पहुँच जाती है।
  • git इंस्टॉलर्स को एक prepare स्क्रिप्ट दें जो बिना dev-only मान्यताओं के बिल्ड हो सके, और उन्हें README में बताएं कि उन्हें अपने प्रोफाइल के pnpm-workspace.yaml में उस बिल्ड को allowlist करना होगा।
  • README पर उस release candidate की तारीख डालें जिसके खिलाफ आपने बिल्ड और टेस्ट किया है। प्रीव्यू API के पाठकों को यह जानना आवश्यक है कि आपने किस वर्जन का उपयोग किया था।

यह देखने के लिए कि बाहर से एक तैयार प्लगइन कैसा दिखता है, इंस्टॉल करने योग्य dsh प्लगइन्स पढ़ें और ध्यान दें कि इंस्टॉल करने से पहले प्रत्येक README आपको क्या बताता है। यदि आपने किसी अन्य एजेंट के लिए एक्सटेंशन लिखे हैं, तो Claude Code प्लगइन्स कैसे बनाए जाते हैं एक उपयोगी तुलना प्रदान करता है। हार्नेस आपको एक लाइव ऑब्जेक्ट ग्राफ और रिवर्सिबल रजिस्ट्रेशन देता है, जो फाइलों के मैनिफेस्ट से कहीं अधिक शक्तिशाली है, और इसके साथ जिम्मेदारी भी अधिक आती है।

FAQ

क्या dsh plugin लिखने के लिए मुझे npm पर publish करना होगा?

नहीं। cordis.yml overlay में एक filesystem path, जिसे dsh web --patch ./scratch-plugin/cordis.yml के साथ load किया गया हो, harness के अंदर अपना code चलाने के लिए पर्याप्त है। Path absolute होना चाहिए। Packaging केवल तब मायने रखती है जब कोई और plugin install करता है, और तब भी आप packaged form का परीक्षण करने के लिए registry को छुए बिना dsh plugin --profile demo add ./my-plugin के साथ local folder install कर सकते हैं।

मेरा plugin load क्यों हो जाता है लेकिन tool कभी दिखाई नहीं देता?

सबसे पहले dsh --profile demo --dump-config चलाएँ। यदि उस output में आपकी row id गायब है, तो plugin कभी mount नहीं हुआ और इसका कारण code के बजाय composition है। यदि row मौजूद है, तो export const inject = ['tools'] की जाँच करें। Cordis configuration में entries एक साथ (concurrently) शुरू होती हैं, इसलिए file का क्रम load होने का क्रम तय नहीं करता। उस declaration के बिना Cordis tool registry का इंतज़ार नहीं करता, और आपका apply ऐसे समय पर चल सकता है जब register करने के लिए ctx.tools उपलब्ध न हो।

cordis.yml और cordis.patch.yml में क्या अंतर है?

cordis.yml एक पूर्ण entry list है। cordis.patch.yml एक layer है जिसे किसी एक पर apply किया जाता है, जो नई rows डालने या मौजूदा configuration को बदलने के लिए id द्वारा rows को target करती है। एक bundle package.json में dsh.bundle.patch के माध्यम से अपनी patch file की ओर इशारा करता है। Layers एक निश्चित क्रम में apply होती हैं: profile की सूचीबद्ध सूची में प्रत्येक bundle, फिर profile की patch file, फिर $DSH_HOME/cordis.patch.yml, और अंत में कोई भी --patch overlay। बाद वाली layers प्रभावी होती हैं।

क्या agent चलते समय मैं dsh plugin को hot reload कर सकता हूँ?

0.1.0-rc.7 के अनुसार, web profile में host side के लिए ऐसा नहीं किया जा सकता। वह bundle shared hot module reload row को disabled करके ship होता है, जिसमें file में एक note है कि यह तब वापस आएगा जब इसका reload lifecycle test हो जाएगा। इसके बजाय तेज़ restart के लिए design करें: एक file, जिसे बिना किसी build step के --patch के माध्यम से load किया जाए, और हर registration ctx के माध्यम से की जाए ताकि एक run से दूसरे run में कुछ भी leak न हो। उन resources के लिए disposer के साथ ctx.effect() का उपयोग करें जिन्हें Cordis अपने आप clean नहीं कर सकता।