{"_id":"@burna-org/ansible-workflows","_rev":"3-4486ec65400fdb5ce8007e1cc6bcd2f0","name":"@burna-org/ansible-workflows","dist-tags":{"latest":"0.0.3"},"versions":{"0.0.1":{"name":"@burna-org/ansible-workflows","version":"0.0.1","keywords":["ansible","awx","workflow","nodejs"],"author":{"name":"Burna Team"},"license":"MIT","_id":"@burna-org/ansible-workflows@0.0.1","maintainers":[{"name":"radiumgh","email":"mail.rezaghanbari@gmail.com"}],"dist":{"shasum":"0b3fdfe2aaf78fc40e8eeda2460390293b8bdbdb","tarball":"https://registry.npmjs.org/@burna-org/ansible-workflows/-/ansible-workflows-0.0.1.tgz","fileCount":20,"integrity":"sha512-r1HiHONeiQQErb63i7meZqcXj3crxiix9HzZiKe3MUyXL9ryBYky+AReBGcAfV1w0wI2G2hEXW19HRuJCRrQzQ==","signatures":[{"sig":"MEYCIQCvJD7D/+thEi84U1bqxxQ5wWn/uBLQEe1JKGFF8DXIzwIhAMiWAIT3jOUfLlpBsxMyATpGQghd/7fldp/46KyeYkwr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57883},"main":"index.js","types":"dist/types/index.d.ts","scripts":{"test":"jest --runInBand","prepack":"npm run clean:types && npm run build:types","typecheck":"tsc -p tsconfig.json --noEmit","build:types":"tsc -p tsconfig.types.json","clean:types":"rm -rf dist/types","test:coverage":"jest --runInBand --coverage"},"_npmUser":{"name":"radiumgh","email":"mail.rezaghanbari@gmail.com"},"_npmVersion":"11.6.2","description":"Ansible workflow helpers","directories":{},"_nodeVersion":"24.11.1","dependencies":{"consola":"^3.4.2","@burna-org/awx-client":"^0.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/ansible-workflows_0.0.1_1771488879919_0.6298784583721979","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@burna-org/ansible-workflows","version":"0.0.2","keywords":["ansible","awx","workflow","nodejs"],"author":{"name":"Burna Team"},"license":"MIT","_id":"@burna-org/ansible-workflows@0.0.2","maintainers":[{"name":"radiumgh","email":"mail.rezaghanbari@gmail.com"}],"dist":{"shasum":"ef5b2f1a849c7c1407be931df3d9636b41f62b88","tarball":"https://registry.npmjs.org/@burna-org/ansible-workflows/-/ansible-workflows-0.0.2.tgz","fileCount":20,"integrity":"sha512-YOmJL/V4/qlZYs+Bm7Dd8BwehhLWI5ZpZRAGlQ61Qy318fNJHYgMrzDG6sqBC4cOBLxx/QN2Ujo2yyToFp/DDQ==","signatures":[{"sig":"MEUCIA42OHBe3Yp9iSd3N/eQ8nGnH7YJhTjbSmPfal84Pd+kAiEA1cc10C4Satf4JfTrdA3BnIit6Y+VGz3kDM710H07gIk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":58349},"main":"index.js","types":"dist/types/index.d.ts","gitHead":"9142dff7069aa06a4c3e2eb410452769eb9e7a42","scripts":{"test":"jest --runInBand","prepack":"npm run clean:types && npm run build:types","typecheck":"tsc -p tsconfig.json --noEmit","build:types":"tsc -p tsconfig.types.json","clean:types":"rm -rf dist/types","test:coverage":"jest --runInBand --coverage"},"_npmUser":{"name":"radiumgh","email":"mail.rezaghanbari@gmail.com"},"_npmVersion":"11.6.2","description":"Ansible workflow helpers","directories":{},"_nodeVersion":"24.11.1","dependencies":{"consola":"^3.4.2","@burna-org/awx-client":"^0.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/ansible-workflows_0.0.2_1771582541388_0.29671491728378685","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@burna-org/ansible-workflows","version":"0.0.3","description":"Ansible workflow helpers","main":"index.js","types":"dist/types/index.d.ts","scripts":{"test":"jest --runInBand","test:coverage":"jest --runInBand --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:types":"tsc -p tsconfig.types.json","clean:types":"rm -rf dist/types","prepack":"npm run clean:types && npm run build:types"},"keywords":["ansible","awx","workflow","nodejs"],"author":{"name":"Burna Team"},"license":"MIT","dependencies":{"@burna-org/awx-client":"^0.0.2","consola":"^3.4.2"},"publishConfig":{"access":"public"},"devDependencies":{"jest":"^29.7.0","typescript":"^5.6.3"},"gitHead":"9142dff7069aa06a4c3e2eb410452769eb9e7a42","_id":"@burna-org/ansible-workflows@0.0.3","_nodeVersion":"24.11.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-Cai7Sth8vJTx9sagYl2PSBwCXIbDfm52kazwkSH0dGUZwjfqi6rhg8T5vGvnUbc22MaNEXE+j/Byzk3DaORtDg==","shasum":"b2da4c483ee62aa6a2200df4b5950af73e78083a","tarball":"https://registry.npmjs.org/@burna-org/ansible-workflows/-/ansible-workflows-0.0.3.tgz","fileCount":20,"unpackedSize":58349,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICVOO6wlThWS2JiGdCiaXg4Eah0ZfLbqN8tDKe4CZOYwAiBSezlkMagaO6kiYG5iXWsopwV9hJtgXjaaEbpTnzSl4g=="}]},"_npmUser":{"name":"radiumgh","email":"mail.rezaghanbari@gmail.com"},"directories":{},"maintainers":[{"name":"radiumgh","email":"mail.rezaghanbari@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ansible-workflows_0.0.3_1771582622093_0.12403481914476"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-19T08:14:39.738Z","modified":"2026-02-20T10:17:02.358Z","0.0.1":"2026-02-19T08:14:40.054Z","0.0.2":"2026-02-20T10:15:41.571Z","0.0.3":"2026-02-20T10:17:02.245Z"},"author":{"name":"Burna Team"},"license":"MIT","keywords":["ansible","awx","workflow","nodejs"],"description":"Ansible workflow helpers","maintainers":[{"name":"radiumgh","email":"mail.rezaghanbari@gmail.com"}],"readme":"# @burna-org/ansible-workflows\n\nAnsible workflow helpers built on top of `@burna-org/awx-client`.\n\nThis library composes:\n\n- Full AWX client API pass-through\n- Higher-level helpers for pagination, job polling, cached job reuse, and template auto-provisioning\n\n## Install\n\n```bash\nnpm install @burna-org/ansible-workflows @burna-org/awx-client\n```\n\n## Quick start (server)\n\n```js\nconst AWXClient = require('@burna-org/awx-client');\nconst {createAnsibleWorkflows} = require('@burna-org/ansible-workflows');\n\nconst awxClient = new AWXClient({\n  host: process.env.AWX_HOST,\n  username: process.env.AWX_USERNAME,\n  password: process.env.AWX_PASSWORD,\n});\n\nconst ansible = createAnsibleWorkflows({\n  awxClient,\n  config: {\n    jobResultExpirySeconds: 3600,\n    pollInitialDelay: 5000,\n    pollInterval: 2000,\n  },\n});\n```\n\n## Factory\n\n`createAnsibleWorkflows({ awxClient, config })`\n\n- `awxClient` (required): an initialized `AWXClient` instance\n- `config.jobResultExpirySeconds` (default `3600`)\n- `config.pollInitialDelay` (default `5000` ms)\n- `config.pollInterval` (default `2000` ms)\n- `config.runtimeMock` (default `false`): when `true`, use built-in mock AWX responses and avoid real HTTP calls.\n\nIf `awxClient` is missing, it throws `awxClient is required` unless `config.runtimeMock` is enabled.\nIf required AWX methods are missing, it throws `awxClient method \"<name>\" is required`.\n\n## Returned API\n\nThe returned object includes:\n\n1. All AWX methods from `@burna-org/awx-client`\n2. Workflow helper methods below\n\n### AWX pass-through methods\n\nAll of these are directly delegated to `awxClient`:\n\n- `getAccessToken`\n- `listInventories`, `listInventoryHosts`, `createInventory`, `manageInventoryHost`\n- `listJobs`, `retrieveJob`, `retrieveJobStdout`, `listJobEvents`\n- `listJobTemplates`, `createJobTemplate`, `updateJobTemplate`, `launchJobTemplate`, `listJobsForTemplate`, `manageJobTemplateCredential`\n- `listAdhocCommands`, `retrieveAdhocCommand`, `retrieveAdhocCommandStdout`, `createAdhocCommand`\n- `listProjects`, `createProject`\n- `listCredentials`, `createCredential`\n- `listOrganizations`, `createOrganization`\n- `updateHost`, `listHosts`, `listHostGroups`\n- `createGroup`, `updateGroup`, `listGroups`, `listGroupHosts`, `manageGroupHost`\n- `listExecutionEnvironments`\n- `listSettingCategories`, `listSettings`, `updateSettings`\n\n## Helper API reference\n\n### `getGroups({ inventoryName })`\n\nReturns all groups for inventory name, handling pagination.\n\nResponse:\n\n```js\n{ groups: Array, error: '' }\n```\n\n### `getHosts({ inventoryName })`\n\nFinds inventory by name, then returns all hosts with pagination.\n\nResponse:\n\n```js\n{ hosts: Array, error: '' }\n```\n\nIf inventory is not found:\n\n```js\n{ hosts: [], error: 'Failed to found inventory \"<name>\"' }\n```\n\n### `findJobTemplateId({ jobTemplateName })`\n\nResponse:\n\n```js\n{ jobTemplateId: number | null, error: string }\n```\n\n### `getLastJobResult({ jobTemplateName, limit })`\n\nReturns latest non-failed job result if not expired.\n\nUses configured `jobResultExpirySeconds`.\n\nResponse:\n\n```js\n{ job: Object | null, error: string }\n```\n\n### `handleJob({ jobTemplateName, limit, options, useLastJobResult })`\n\nFlow:\n\n1. Optionally reuse valid previous job\n2. Else launch new job template run\n3. Poll until finished\n\n`useLastJobResult` defaults to `true`.\n\nResponse:\n\n```js\n{ job: Object | null, error: string }\n```\n\n### `handleJobEvents({ jobTemplateName, limit, options, urlParams, useLastJobResult })`\n\nRuns/reuses job via `handleJob`, then fetches all events with pagination.\n\nResponse:\n\n```js\n{ jobEvents: Array, error: string }\n```\n\n### `getJobEventsByJobId({ jobId, urlParams })`\n\nPolls job by ID until completion, then returns all events.\n\nResponse:\n\n```js\n{ jobEvents: Array, error: '' }\n// or\n{ job: null, error: string }\n```\n\n### `waitForJobToFinish({ jobId })`\n\nPolls AWX until job finishes.\n\nResponse:\n\n```js\n{ job: Object | null, error: string }\n```\n\n### `ensureJobTemplate({ ... })`\n\nEnsures template exists and returns template ID. If missing, it creates template and attaches credentials.\n\nParams:\n\n- `slug` (required): logical template key (for example `deploy_app`)\n- `templateNamePrefix` (optional): prefix for generated name\n- `projectName` (required)\n- `inventoryName` (required)\n- `executionEnvironmentName` (required)\n- `credentialNames` (required): comma-separated string or array\n- `playbook` (required)\n- `extraVars` (optional object)\n- `options` (optional AWX template options)\n\nNotes:\n\n- Template display name is generated from prefix+slug and title-cased\n- Uses in-memory cache for template IDs\n- Throws on resolution/create failures\n\n### `clearTemplateCache(slug, templateNamePrefix)`\n\n- With `slug`, clears one cached entry\n- With no args, clears all cache\n\n### `getTemplateCacheStats()`\n\nReturns:\n\n```js\n{ size: number, keys: string[] }\n```\n\n## Practical examples\n\n### 1) Use helper APIs\n\n```js\nconst {groups, error} = await ansible.getGroups({inventoryName: 'production'});\nif (error) throw new Error(error);\n\nconst hostsResult = await ansible.getHosts({inventoryName: 'production'});\n```\n\n### 2) Run job with cache fallback\n\n```js\nconst result = await ansible.handleJob({\n  jobTemplateName: 'Deploy App',\n  limit: 'web-01',\n  options: {\n    extra_vars: {release: '2026.02'},\n  },\n  useLastJobResult: true,\n});\n\nif (result.error) {\n  throw new Error(result.error);\n}\n\nconsole.log('Job ID:', result.job.id);\n```\n\n### 3) Run and collect all events\n\n```js\nconst events = await ansible.handleJobEvents({\n  jobTemplateName: 'Deploy App',\n  urlParams: {\n    event: 'runner_on_ok',\n    page_size: 200,\n  },\n});\n\nif (events.error) {\n  throw new Error(events.error);\n}\n\nconsole.log(events.jobEvents.length);\n```\n\n### 4) Ensure template exists (auto-create)\n\n```js\nconst templateId = await ansible.ensureJobTemplate({\n  slug: 'deploy_app',\n  templateNamePrefix: 'kolla-',\n  projectName: 'infra-project',\n  inventoryName: 'prod-inventory',\n  executionEnvironmentName: 'default-ee',\n  credentialNames: ['machine-cred', 'vault-cred'],\n  playbook: 'site.yml',\n  extraVars: {kolla_action: 'deploy'},\n  options: {verbosity: 2},\n});\n\nconsole.log('Template ID:', templateId);\n```\n\n### 5) Cache inspection/cleanup\n\n```js\nconsole.log(ansible.getTemplateCacheStats());\nansible.clearTemplateCache('deploy_app', 'kolla-');\nansible.clearTemplateCache();\n```\n\n## Example server wrapper (Express)\n\n```js\nconst express = require('express');\nconst app = express();\n\napp.use(express.json());\n\napp.post('/api/ansible/job', async (req, res) => {\n  const {jobTemplateName, limit, options} = req.body;\n\n  const result = await ansible.handleJob({\n    jobTemplateName,\n    limit,\n    options,\n    useLastJobResult: true,\n  });\n\n  if (result.error) {\n    return res.status(400).json(result);\n  }\n\n  return res.json(result);\n});\n\napp.get('/api/ansible/job/:id/events', async (req, res) => {\n  const result = await ansible.getJobEventsByJobId({\n    jobId: Number(req.params.id),\n    urlParams: req.query,\n  });\n\n  if (result.error) {\n    return res.status(400).json(result);\n  }\n\n  return res.json(result);\n});\n```\n\n## Error handling model\n\n- Most helpers return `{..., error: ''}` on success and a non-empty `error` string on failures\n- `ensureJobTemplate` throws when required dependencies/resources cannot be resolved\n\n## Development\n\n```bash\nnpm test\nnpm run typecheck\nnpm run build:types\n```\n","readmeFilename":"README.md"}