@nullplatform/mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/config.js +26 -0
- package/dist/git.js +27 -0
- package/dist/http.js +330 -0
- package/dist/i18n.js +595 -0
- package/dist/index.js +72 -0
- package/dist/md.js +110 -0
- package/dist/np/auth.js +130 -0
- package/dist/np/client.js +72 -0
- package/dist/np/context.js +201 -0
- package/dist/np/journey.js +403 -0
- package/dist/prompts.js +64 -0
- package/dist/render.js +236 -0
- package/dist/server.js +91 -0
- package/dist/skills.js +84 -0
- package/dist/surfaces/developer.js +29 -0
- package/dist/surfaces/index.js +17 -0
- package/dist/surfaces/surface.js +1 -0
- package/dist/tool-names.js +25 -0
- package/dist/tool.js +92 -0
- package/dist/tools/approvals.js +80 -0
- package/dist/tools/builds.js +94 -0
- package/dist/tools/create-app.js +187 -0
- package/dist/tools/create-release.js +52 -0
- package/dist/tools/create-scope.js +82 -0
- package/dist/tools/deploy.js +178 -0
- package/dist/tools/find-apps.js +36 -0
- package/dist/tools/index.js +39 -0
- package/dist/tools/logs.js +83 -0
- package/dist/tools/metrics.js +83 -0
- package/dist/tools/overview.js +110 -0
- package/dist/tools/params.js +58 -0
- package/dist/tools/playbook.js +39 -0
- package/dist/tools/services.js +58 -0
- package/dist/tools/set-params.js +58 -0
- package/dist/tools/shared.js +141 -0
- package/dist/tools/status.js +70 -0
- package/dist/tools/traffic.js +74 -0
- package/dist/ui.js +76 -0
- package/package.json +65 -0
- package/skills/deploying-safely/SKILL.md +54 -0
- package/skills/incident-response/SKILL.md +52 -0
- package/skills/platform-conventions/SKILL.md +61 -0
- package/widgets-dist/create-app.html +830 -0
- package/widgets-dist/find-apps.html +831 -0
- package/widgets-dist/logs.html +830 -0
- package/widgets-dist/manifest.json +8 -0
- package/widgets-dist/metrics.html +829 -0
- package/widgets-dist/np-panel.html +831 -0
- package/widgets-dist/params.html +829 -0
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
/**
|
|
3
|
+
* i18n for everything user-facing: tool reply markdown, ask-backs, error messages,
|
|
4
|
+
* prompt bodies and HTTP error hints. Model-facing text (tool names, descriptions,
|
|
5
|
+
* schema descriptions, skills) deliberately stays English — it is prompt material.
|
|
6
|
+
*
|
|
7
|
+
* Locale: stdio reads NP_LANG/LANG once at startup; HTTP resolves Accept-Language per
|
|
8
|
+
* request and scopes it with AsyncLocalStorage. Adding a locale = adding one catalog
|
|
9
|
+
* object below — completeness is compile-checked against the English catalog.
|
|
10
|
+
*/
|
|
11
|
+
const english = {
|
|
12
|
+
// — resolution (which app?) —
|
|
13
|
+
"resolve.ambiguous": "Several applications match — say which one (name or `#id`):",
|
|
14
|
+
"resolve.noId": "No application with id {id}.",
|
|
15
|
+
"resolve.noMatch": 'No application matching "{ref}".',
|
|
16
|
+
"resolve.repoUnlinked": 'This repo ({url}) isn\'t linked to any nullplatform application. Pass app:"<name>" or run application_create to link it.',
|
|
17
|
+
"resolve.noInput": 'No app given and no git remote found in the workspace. Pass app:"<name>" (or an id like "#123").',
|
|
18
|
+
"resolve.noNrn": "Can't resolve the application NRN for **{app}**.",
|
|
19
|
+
// — scope selection —
|
|
20
|
+
"scope.which": "Which scope?",
|
|
21
|
+
"scope.none": "This app has no scopes yet.",
|
|
22
|
+
"scope.noneHint": '`application_scope_create name:"dev"` first — then `application_deployment_create`.',
|
|
23
|
+
"scope.noMatch": 'No scope matching "{wanted}". Scopes: {names}.',
|
|
24
|
+
"scope.noMatchDetailed": 'No scope matching "{wanted}" (by name or dimension). Scopes:',
|
|
25
|
+
"scope.createHint": '`application_scope_create name:"{name}"` to create it.',
|
|
26
|
+
"scope.deployHint": '`application_deployment_create scope:"<name or dimension, e.g. environment=production>"`',
|
|
27
|
+
"scope.perScope": "{noun} are per scope — which one?",
|
|
28
|
+
"scope.readHint": '`{tool} scope:"{name}"`',
|
|
29
|
+
// — table headers —
|
|
30
|
+
"header.app": "App",
|
|
31
|
+
"header.id": "Id",
|
|
32
|
+
"header.namespace": "Namespace",
|
|
33
|
+
"header.account": "Account",
|
|
34
|
+
"header.status": "Status",
|
|
35
|
+
"header.scope": "Scope",
|
|
36
|
+
"header.dimensions": "Dimensions",
|
|
37
|
+
"header.name": "Name",
|
|
38
|
+
"header.variable": "Variable",
|
|
39
|
+
"header.type": "Type",
|
|
40
|
+
"header.secret": "Secret",
|
|
41
|
+
"header.value": "Value",
|
|
42
|
+
"header.metric": "Metric",
|
|
43
|
+
"header.now": "Now",
|
|
44
|
+
"header.avg": "Avg",
|
|
45
|
+
"header.max": "Max",
|
|
46
|
+
"header.trend": "Trend",
|
|
47
|
+
"header.asset": "Asset",
|
|
48
|
+
"header.platform": "Platform",
|
|
49
|
+
"header.deployment": "Deployment",
|
|
50
|
+
"header.provider": "Provider",
|
|
51
|
+
"header.live": "Live",
|
|
52
|
+
"header.traffic": "Traffic",
|
|
53
|
+
"header.when": "When",
|
|
54
|
+
// — markdown design language —
|
|
55
|
+
"md.next": "Next",
|
|
56
|
+
"md.none": "_none_",
|
|
57
|
+
"md.justNow": "just now",
|
|
58
|
+
"md.minutesAgo": "{count}m ago",
|
|
59
|
+
"md.hoursAgo": "{count}h ago",
|
|
60
|
+
"md.daysAgo": "{count}d ago",
|
|
61
|
+
"md.dashboard": "dashboard",
|
|
62
|
+
"md.unknown": "unknown",
|
|
63
|
+
// — platform status words (fallback: raw status with underscores spaced) —
|
|
64
|
+
"status.active": "active",
|
|
65
|
+
"status.successful": "successful",
|
|
66
|
+
"status.finalized": "finalized",
|
|
67
|
+
"status.running": "running",
|
|
68
|
+
"status.pending": "pending",
|
|
69
|
+
"status.creating": "creating",
|
|
70
|
+
"status.updating": "updating",
|
|
71
|
+
"status.building": "building",
|
|
72
|
+
"status.waiting_for_instances": "waiting for instances",
|
|
73
|
+
"status.switching_traffic": "switching traffic",
|
|
74
|
+
"status.finalizing": "finalizing",
|
|
75
|
+
"status.cancelling": "cancelling",
|
|
76
|
+
"status.rolling_back": "rolling back",
|
|
77
|
+
"status.creating_approval": "waiting for approval",
|
|
78
|
+
"status.failed": "failed",
|
|
79
|
+
"status.cancelled": "cancelled",
|
|
80
|
+
"status.rolled_back": "rolled back",
|
|
81
|
+
"status.deleted": "deleted",
|
|
82
|
+
"status.inactive": "inactive",
|
|
83
|
+
"status.creating_approval_denied": "approval denied",
|
|
84
|
+
// — status view —
|
|
85
|
+
"render.noScopes": "_No scopes yet — nothing to deploy onto._",
|
|
86
|
+
"render.nothingDeployed": "_nothing deployed_",
|
|
87
|
+
"render.noBuilds": "_none — push to CI_",
|
|
88
|
+
"render.latestBuild": "Latest build",
|
|
89
|
+
"render.releases": "Releases",
|
|
90
|
+
"render.liveOn": "live on {scopes}",
|
|
91
|
+
"render.deployment": "**Deployment**",
|
|
92
|
+
"render.deploying": "🚀 **Deploying**",
|
|
93
|
+
"render.release": "Release",
|
|
94
|
+
"render.started": "started {when}",
|
|
95
|
+
"render.now": "now",
|
|
96
|
+
"render.desired": "→ desired {pct}%",
|
|
97
|
+
// — next-step hints (status) —
|
|
98
|
+
"hint.rolloutTraffic": 'deployment #{id} on **{scope}** is at {traffic}% traffic — `application_deployment_update percent:<n>` to advance, `application_deployment_update action:"finalize"` when done, `application_deployment_update action:"rollback"` to back out.',
|
|
99
|
+
"hint.rolloutStatus": "deployment #{id} on **{scope}** is {status} — `application_get deployment:{id}` to watch.",
|
|
100
|
+
"hint.noScopes": 'no scopes yet — `application_scope_create name:"dev"` to get a place to deploy.',
|
|
101
|
+
"hint.buildNewer": "build #{id} ({branch} @{commit}) is newer than release {semver} — `application_deployment_create` ships it (creates the release for you).",
|
|
102
|
+
"hint.buildUnreleased": "build #{id} has no release yet — `application_deployment_create` ships it (creates the release for you).",
|
|
103
|
+
"hint.releaseNotLive": "release {semver} isn't live yet — `application_deployment_create` to ship it.",
|
|
104
|
+
"hint.noBuilds": "no builds yet — push a commit so CI produces one, then `application_deployment_create`.",
|
|
105
|
+
"hint.upToDate": "everything is up to date ✅",
|
|
106
|
+
"hint.watch": "`application_get deployment:{id}` to watch.",
|
|
107
|
+
// — next-step hints (rollout) —
|
|
108
|
+
"rollout.full": 'all traffic on the new version — `application_deployment_update action:"finalize"` to retire the old one.',
|
|
109
|
+
"rollout.moving": "traffic moving {from}% → {to}% — `application_get deployment:{id}` to watch.",
|
|
110
|
+
"rollout.at": 'traffic at {traffic}% — `application_deployment_update percent:<n>` to advance (marks: 1,5,10,25,50,75,90,95,99,100), `application_deployment_update action:"rollback"` to back out.',
|
|
111
|
+
"rollout.instances": "instances are coming up — `application_get deployment:{id}` to watch.",
|
|
112
|
+
"rollout.switching": "traffic is switching — `application_get deployment:{id}` to watch.",
|
|
113
|
+
"rollout.finalizing": "finalizing — old instances are being retired.",
|
|
114
|
+
"rollout.done": "done — the release is fully live ✅",
|
|
115
|
+
"rollout.approval": "waiting for an approval ✋ — an approver must allow this deployment (dashboard → Approvals).",
|
|
116
|
+
"rollout.failed": 'deployment failed — `application_log_list` for output, `application_deployment_update action:"rollback"` if traffic moved.',
|
|
117
|
+
"rollout.rolledBack": "rolled back — previous version is serving all traffic.",
|
|
118
|
+
// — application_get tool —
|
|
119
|
+
"status.errorLabel": "Couldn't load status",
|
|
120
|
+
// — application_list tool —
|
|
121
|
+
"findApps.noneMatching": "No applications{matching}{inNamespace}.",
|
|
122
|
+
"findApps.matching": ' matching "{query}"',
|
|
123
|
+
"findApps.inNamespace": ' in namespaces ~"{namespace}"',
|
|
124
|
+
"findApps.createHint": "`application_create` links this repo as a new application.",
|
|
125
|
+
"findApps.count.one": "1 application",
|
|
126
|
+
"findApps.count.many": "{count} applications",
|
|
127
|
+
"findApps.statusHint": '`application_get app:"<name>"` for the full picture of one.',
|
|
128
|
+
"findApps.truncated": "Showing the first {shown} — narrow with a query or namespace to see the rest.",
|
|
129
|
+
"findApps.errorLabel": "Search failed",
|
|
130
|
+
// — application_parameter_list tool —
|
|
131
|
+
"params.unavailable": "Parameter listing isn't available here. `application_parameter_create` still works.",
|
|
132
|
+
"params.viewInDashboard": "view in dashboard",
|
|
133
|
+
"params.none": "**{app}** has no parameters.",
|
|
134
|
+
"params.noneHint": "`application_parameter_create` to add env vars or files.",
|
|
135
|
+
"params.count.one": "**{app}** · 1 parameter",
|
|
136
|
+
"params.count.many": "**{app}** · {count} parameters",
|
|
137
|
+
"params.unset": "_unset_",
|
|
138
|
+
"params.applyHint": "changed values apply on the next `application_deployment_create`.",
|
|
139
|
+
"params.errorLabel": "Couldn't list parameters",
|
|
140
|
+
// — application_log_list tool —
|
|
141
|
+
"logs.noun": "Logs",
|
|
142
|
+
"logs.noScopes": "**{app}** has no scopes, so there are no runtime logs yet.",
|
|
143
|
+
"logs.noScopesHint": "`application_deployment_create` after creating a scope — logs appear once something runs.",
|
|
144
|
+
"logs.empty": "No log lines returned for **{app}**{scope}.",
|
|
145
|
+
"logs.openInDashboard": "open logs in dashboard",
|
|
146
|
+
"logs.lastLines": "**{app}**{scope} — last {count} lines",
|
|
147
|
+
"logs.errorLabel": "Couldn't read logs",
|
|
148
|
+
"logs.errorSuffix": "The dashboard's Logs view may still have them.",
|
|
149
|
+
// — application_metric_list tool —
|
|
150
|
+
"metrics.noun": "Metrics",
|
|
151
|
+
"metrics.noScopes": "**{app}** has no scopes, so there are no runtime metrics yet.",
|
|
152
|
+
"metrics.noScopesHint": "`application_deployment_create` after creating a scope.",
|
|
153
|
+
"metrics.empty": "No metric datapoints for **{app}** · {scope} in the last {window} — the scope may be young or the metrics agent not wired.",
|
|
154
|
+
"metrics.openInDashboard": "performance in dashboard",
|
|
155
|
+
"metrics.title": "**{app}** · {scope} — last {window}",
|
|
156
|
+
"metrics.errorLabel": "Couldn't load metrics",
|
|
157
|
+
// — application_build_list tool —
|
|
158
|
+
"builds.title": "**{app}** · {count} recent build(s)",
|
|
159
|
+
"builds.branch": "Branch",
|
|
160
|
+
"builds.commit": "Commit",
|
|
161
|
+
"builds.released": "Released",
|
|
162
|
+
"builds.none": "**{app}** has no builds yet.",
|
|
163
|
+
"builds.noneHint": "push a commit so CI produces one, then `application_deployment_create`.",
|
|
164
|
+
"builds.deployHint": "`application_deployment_create build_id:{build}` ships it (cuts the release for you).",
|
|
165
|
+
"builds.waiting": "build #{build} is still running — `application_build_list` again in a moment to check.",
|
|
166
|
+
"builds.upToDate": "the latest successful build is already released.",
|
|
167
|
+
"builds.assetsTitle": "Assets of build #{build}",
|
|
168
|
+
"builds.noAssets": "Build #{build} has no assets (it may still be building).",
|
|
169
|
+
"builds.errorLabel": "Couldn't list builds",
|
|
170
|
+
// — organization_get tool —
|
|
171
|
+
"overview.title": "Organization overview · {count} application(s) scanned",
|
|
172
|
+
"overview.truncated": '_(showing the first {count} — narrow with `organization_get query:"..."`)_',
|
|
173
|
+
"overview.activeRollouts": "Active rollouts ({count})",
|
|
174
|
+
"overview.trouble": "Needs attention ({count})",
|
|
175
|
+
"overview.hintActive": '`application_get app:"{app}"` to watch a rollout, or `application_deployment_update` to drive one.',
|
|
176
|
+
"overview.hintTrouble": '`application_log_list app:"{app}"` to investigate, `application_deployment_update action:"rollback"` if needed.',
|
|
177
|
+
"overview.hintCalm": "everything is steady — nothing rolling out, nothing failed.",
|
|
178
|
+
"overview.errorLabel": "Couldn't build the overview",
|
|
179
|
+
// — application_approval_list tool —
|
|
180
|
+
"approvals.title": "**{app}** · {count} approval(s)",
|
|
181
|
+
"approvals.action": "Action",
|
|
182
|
+
"approvals.entity": "Entity",
|
|
183
|
+
"approvals.requestedBy": "Requested by",
|
|
184
|
+
"approvals.none": "No pending approvals for **{app}**.",
|
|
185
|
+
"approvals.needId": "Pass `approval_id` together with the action.",
|
|
186
|
+
"approvals.invalidId": "That doesn't look like a valid approval id — copy it from the `application_approval_list` list.",
|
|
187
|
+
"approvals.approved": "Approved {id} — the gated action can proceed.",
|
|
188
|
+
"approvals.cancelled": "Cancelled approval {id}.",
|
|
189
|
+
"approvals.actHint": '`application_approval_list action:"approve" approval_id:"{id}"` to let it proceed (uses your permissions).',
|
|
190
|
+
"approvals.errorLabel": "Couldn't load approvals",
|
|
191
|
+
// — application_service_list tool —
|
|
192
|
+
"services.attached": "**{app}** · {count} dependency service(s)",
|
|
193
|
+
"services.specification": "Type",
|
|
194
|
+
"services.none": "**{app}** has no dependency services attached.",
|
|
195
|
+
"services.catalog": "Available to provision: {catalog}.",
|
|
196
|
+
"services.provisionHint": "provisioning a dependency is a guided flow — create one in the dashboard.",
|
|
197
|
+
"services.errorLabel": "Couldn't load services",
|
|
198
|
+
// — application_deployment_create tool —
|
|
199
|
+
"deploy.noScopeTypes": "This app has no scopes yet. Available scope types: {types}.",
|
|
200
|
+
"deploy.createScopeHint": '`application_scope_create name:"dev" type:"{type}"`',
|
|
201
|
+
"deploy.usingRelease": "Using existing release **{semver}** (#{id}). ",
|
|
202
|
+
"deploy.createdRelease": "Created release **{semver}** from build #{build}. ",
|
|
203
|
+
"deploy.createdReleaseFrom": "Created release **{semver}** from build #{build} ({branch} @{commit}). ",
|
|
204
|
+
"deploy.noSuchVersion": "No release **{version}** exists{known} and there's no successful build to cut it from.",
|
|
205
|
+
"deploy.knownVersions": " (known: {versions})",
|
|
206
|
+
"deploy.nothing": "Nothing to deploy: no active releases and no successful builds.",
|
|
207
|
+
"deploy.nothingHint": "push a commit so CI produces a build, then `application_deployment_create` again.",
|
|
208
|
+
"deploy.multiAsset": "Build #{build} has several assets — which one ships to **{scope}** ({type})?",
|
|
209
|
+
"deploy.assetHint": '`application_deployment_create asset:"{name}"`',
|
|
210
|
+
"deploy.errorLabel": "Deploy failed",
|
|
211
|
+
"deploy.alreadyRolling": "This release is already rolling out on **{scope}** — showing that deployment.",
|
|
212
|
+
"deploy.rejected": 'Deploy rejected: {message}\nThis usually means the asset choice, not the ids — the build likely has several assets. Retry with `application_deployment_create asset:"<name>"` (see them via `application_get`).',
|
|
213
|
+
// — application_deployment_update tool —
|
|
214
|
+
"traffic.sayWhat": 'Say what to do: `percent:<0-100>`, `action:"finalize"` or `action:"rollback"`.',
|
|
215
|
+
"traffic.noActive": "No active rollout on **{app}** — `application_deployment_create` starts one.",
|
|
216
|
+
"traffic.several": "Several rollouts are active — pass deployment_id:",
|
|
217
|
+
"traffic.snapped": "({requested}% snapped to {snapped}%)",
|
|
218
|
+
"traffic.errorLabel": "Traffic action failed",
|
|
219
|
+
// — application_release_create tool —
|
|
220
|
+
"createRelease.noBuilds": "No successful builds on **{app}** — push a commit so CI produces one.",
|
|
221
|
+
"createRelease.done": "Release **{semver}** created{from}.",
|
|
222
|
+
"createRelease.exists": "Release **{semver}** already exists for build #{build} — reusing it.",
|
|
223
|
+
"createRelease.from": " from build #{build} ({branch} @{commit}, {when})",
|
|
224
|
+
"createRelease.deployHint": "`application_deployment_create release_id:{id}` to ship it.",
|
|
225
|
+
"createRelease.errorLabel": "Couldn't create release",
|
|
226
|
+
// — application_parameter_create tool —
|
|
227
|
+
"setParams.created": "Set {count} new parameter(s) on **{app}**: {names}.",
|
|
228
|
+
"setParams.updated": "Updated {count} existing parameter(s) on **{app}**: {names}.",
|
|
229
|
+
"setParams.mixed": "Set {created} new and updated {updated} parameter(s) on **{app}**: {names}.",
|
|
230
|
+
"setParams.applyHint": "values apply on the next `application_deployment_create`.",
|
|
231
|
+
"setParams.partial": "Parameter update failed partway: {message}\nAlready-created parameters remain — `application_parameter_list` to inspect.",
|
|
232
|
+
"setParams.errorLabel": "Parameter update failed",
|
|
233
|
+
// — application_create tool —
|
|
234
|
+
"createApp.form": "To create an application I need a git repository URL and a name (the form collects them).\nNamespaces: {namespaces}.",
|
|
235
|
+
"createApp.noName": "Couldn't derive a name from {url} — pass name.",
|
|
236
|
+
"createApp.noNamespaceMatch": 'No namespace matching "{namespace}". Namespaces: {names}.',
|
|
237
|
+
"createApp.whichNamespace": "Which namespace?",
|
|
238
|
+
"createApp.whichNamespaceFor": "Which namespace should own **{name}**?",
|
|
239
|
+
"createApp.namespaceHint": '`application_create namespace:"<name>"`',
|
|
240
|
+
"createApp.noNamespaces": "This organization has no namespaces yet — create one in the dashboard first.",
|
|
241
|
+
"createApp.created": "**{name}** created (#{id}) in namespace **{namespace}**, linked to {repo}.",
|
|
242
|
+
"createApp.alreadyLinked": "**{name}** (#{id}) is already linked to {repo} — nothing to create.",
|
|
243
|
+
"createApp.createdHint": "push a commit to trigger the first build, then `application_deployment_create`.",
|
|
244
|
+
"createApp.pending": "⏳ **{name}** created (#{id}), provisioning is still running (status: {status}).",
|
|
245
|
+
"createApp.pendingHint": '`application_get app:"#{id}"` to check on it.',
|
|
246
|
+
"createApp.errorLabel": "Couldn't create the application",
|
|
247
|
+
// — playbook_get tool —
|
|
248
|
+
"playbook.errorLabel": "Couldn't load the playbook",
|
|
249
|
+
// — application_scope_create tool —
|
|
250
|
+
"createScope.whichType": "Which scope type for **{name}**?",
|
|
251
|
+
"createScope.typeHint": '`application_scope_create name:"{name}" type:"<type>"`',
|
|
252
|
+
"createScope.noTypes": 'No scope types available — pass type explicitly (e.g. type:"web_pool").',
|
|
253
|
+
"createScope.provisioning": "⏳ Scope **{name}** (#{id}, {type}{dimensions}) is provisioning — infrastructure is being created.",
|
|
254
|
+
"createScope.exists": "Scope **{name}** (#{id}) already exists ({status}) — nothing to create.",
|
|
255
|
+
"createScope.provisioningHint": '`application_get` shows it; once active, `application_deployment_create scope:"{name}"`.',
|
|
256
|
+
"createScope.errorLabel": "Couldn't create scope",
|
|
257
|
+
// — auth & transport —
|
|
258
|
+
"error.credentialRejected": "nullplatform rejected the credential ({status}) — check your NP_API_KEY",
|
|
259
|
+
"error.permission": "your nullplatform user doesn't have permission for this action (403)",
|
|
260
|
+
"error.bearerNoOrg": "the bearer is not a nullplatform-issued token (no organization claim)",
|
|
261
|
+
"http.authRequired": "authentication required",
|
|
262
|
+
"http.authHint": "send your own nullplatform credential: `Authorization: Bearer <NP_API_KEY>` (or X-NP-API-Key)",
|
|
263
|
+
"http.originDenied": "origin not allowed",
|
|
264
|
+
"http.bodyTooLarge": "request body too large",
|
|
265
|
+
"http.invalidJson": "invalid JSON body",
|
|
266
|
+
"http.verifyUnavailable": "could not verify the credential with nullplatform",
|
|
267
|
+
"http.rateLimited": "too many requests — slow down",
|
|
268
|
+
// — prompts (inserted as the user's message) —
|
|
269
|
+
"prompt.ship": `Ship the latest code of {app}{scope}.
|
|
270
|
+
1. Run status first and tell me what will ship (build/release).
|
|
271
|
+
2. deploy it.
|
|
272
|
+
3. Walk traffic up in steps — 25%, then 50%, then 100% — checking status between steps; stop and tell me if anything looks unhealthy.
|
|
273
|
+
4. When 100% is healthy, finalize and confirm with a final status.`,
|
|
274
|
+
"prompt.ship.toScope": ' to the "{scope}" scope',
|
|
275
|
+
"prompt.thisRepoApp": "the application linked to this repo",
|
|
276
|
+
"prompt.setup": `Connect this repository to nullplatform:
|
|
277
|
+
1. Run application_get — if the repo is already linked to an application, just show me where I stand.
|
|
278
|
+
2. If it isn't linked, check application_list for an app matching this repo's name before creating anything.
|
|
279
|
+
3. If none exists, application_create to link it, then show application_get and tell me what happens next (first build via CI, then deploy).`,
|
|
280
|
+
"prompt.health": `Give me a health check of {app}:
|
|
281
|
+
1. status — what's live where, anything mid-rollout?
|
|
282
|
+
2. metrics (3h window) — call out anything unusual in throughput, response time, error rate, CPU or memory.
|
|
283
|
+
3. logs — scan the recent lines for errors or warnings.
|
|
284
|
+
Finish with a one-paragraph verdict: healthy / degraded / in trouble, and the single most useful next action.`,
|
|
285
|
+
"prompt.rollback": `Something is wrong — roll back {app} right now: find its active rollout, run application_deployment_update action:"rollback", then confirm with application_get that the previous version is serving traffic and summarize what was rolled back.`,
|
|
286
|
+
};
|
|
287
|
+
const spanish = {
|
|
288
|
+
"resolve.ambiguous": "Varias aplicaciones coinciden — decime cuál (nombre o `#id`):",
|
|
289
|
+
"resolve.noId": "No hay ninguna aplicación con id {id}.",
|
|
290
|
+
"resolve.noMatch": 'No hay ninguna aplicación que coincida con "{ref}".',
|
|
291
|
+
"resolve.repoUnlinked": 'Este repo ({url}) no está vinculado a ninguna aplicación de nullplatform. Pasá app:"<nombre>" o ejecutá application_create para vincularlo.',
|
|
292
|
+
"resolve.noInput": 'No se indicó app y no hay git remote en el workspace. Pasá app:"<nombre>" (o un id como "#123").',
|
|
293
|
+
"resolve.noNrn": "No puedo resolver el NRN de la aplicación **{app}**.",
|
|
294
|
+
"scope.which": "¿Qué scope?",
|
|
295
|
+
"scope.none": "Esta app todavía no tiene scopes.",
|
|
296
|
+
"scope.noneHint": '`application_scope_create name:"dev"` primero — después `application_deployment_create`.',
|
|
297
|
+
"scope.noMatch": 'Ningún scope coincide con "{wanted}". Scopes: {names}.',
|
|
298
|
+
"scope.noMatchDetailed": 'Ningún scope coincide con "{wanted}" (por nombre o dimensión). Scopes:',
|
|
299
|
+
"scope.createHint": '`application_scope_create name:"{name}"` para crearlo.',
|
|
300
|
+
"scope.deployHint": '`application_deployment_create scope:"<nombre o dimensión, p. ej. environment=production>"`',
|
|
301
|
+
"scope.perScope": "{noun} son por scope — ¿cuál?",
|
|
302
|
+
"scope.readHint": '`{tool} scope:"{name}"`',
|
|
303
|
+
"header.app": "App",
|
|
304
|
+
"header.id": "Id",
|
|
305
|
+
"header.namespace": "Namespace",
|
|
306
|
+
"header.account": "Cuenta",
|
|
307
|
+
"header.status": "Estado",
|
|
308
|
+
"header.scope": "Scope",
|
|
309
|
+
"header.dimensions": "Dimensiones",
|
|
310
|
+
"header.name": "Nombre",
|
|
311
|
+
"header.variable": "Variable",
|
|
312
|
+
"header.type": "Tipo",
|
|
313
|
+
"header.secret": "Secreto",
|
|
314
|
+
"header.value": "Valor",
|
|
315
|
+
"header.metric": "Métrica",
|
|
316
|
+
"header.now": "Ahora",
|
|
317
|
+
"header.avg": "Prom",
|
|
318
|
+
"header.max": "Máx",
|
|
319
|
+
"header.trend": "Tendencia",
|
|
320
|
+
"header.asset": "Asset",
|
|
321
|
+
"header.platform": "Plataforma",
|
|
322
|
+
"header.deployment": "Deployment",
|
|
323
|
+
"header.provider": "Proveedor",
|
|
324
|
+
"header.live": "En vivo",
|
|
325
|
+
"header.traffic": "Tráfico",
|
|
326
|
+
"header.when": "Cuándo",
|
|
327
|
+
"md.next": "Siguiente",
|
|
328
|
+
"md.none": "_ninguno_",
|
|
329
|
+
"md.justNow": "recién",
|
|
330
|
+
"md.minutesAgo": "hace {count}m",
|
|
331
|
+
"md.hoursAgo": "hace {count}h",
|
|
332
|
+
"md.daysAgo": "hace {count}d",
|
|
333
|
+
"md.dashboard": "dashboard",
|
|
334
|
+
"md.unknown": "desconocido",
|
|
335
|
+
"status.active": "activo",
|
|
336
|
+
"status.successful": "exitoso",
|
|
337
|
+
"status.finalized": "finalizado",
|
|
338
|
+
"status.running": "corriendo",
|
|
339
|
+
"status.pending": "pendiente",
|
|
340
|
+
"status.creating": "creando",
|
|
341
|
+
"status.updating": "actualizando",
|
|
342
|
+
"status.building": "compilando",
|
|
343
|
+
"status.waiting_for_instances": "esperando instancias",
|
|
344
|
+
"status.switching_traffic": "moviendo tráfico",
|
|
345
|
+
"status.finalizing": "finalizando",
|
|
346
|
+
"status.cancelling": "cancelando",
|
|
347
|
+
"status.rolling_back": "revirtiendo",
|
|
348
|
+
"status.creating_approval": "esperando aprobación",
|
|
349
|
+
"status.failed": "falló",
|
|
350
|
+
"status.cancelled": "cancelado",
|
|
351
|
+
"status.rolled_back": "revertido",
|
|
352
|
+
"status.deleted": "eliminado",
|
|
353
|
+
"status.inactive": "inactivo",
|
|
354
|
+
"status.creating_approval_denied": "aprobación denegada",
|
|
355
|
+
"render.noScopes": "_Sin scopes todavía — no hay dónde deployar._",
|
|
356
|
+
"render.nothingDeployed": "_nada deployado_",
|
|
357
|
+
"render.noBuilds": "_ninguno — pusheá para que CI compile_",
|
|
358
|
+
"render.latestBuild": "Último build",
|
|
359
|
+
"render.releases": "Releases",
|
|
360
|
+
"render.liveOn": "en vivo en {scopes}",
|
|
361
|
+
"render.deployment": "**Deployment**",
|
|
362
|
+
"render.deploying": "🚀 **Deployando**",
|
|
363
|
+
"render.release": "Release",
|
|
364
|
+
"render.started": "empezó {when}",
|
|
365
|
+
"render.now": "ahora",
|
|
366
|
+
"render.desired": "→ deseado {pct}%",
|
|
367
|
+
"hint.rolloutTraffic": 'el deployment #{id} en **{scope}** está al {traffic}% de tráfico — `application_deployment_update percent:<n>` para avanzar, `application_deployment_update action:"finalize"` al terminar, `application_deployment_update action:"rollback"` para volver atrás.',
|
|
368
|
+
"hint.rolloutStatus": "el deployment #{id} en **{scope}** está {status} — `application_get deployment:{id}` para seguirlo.",
|
|
369
|
+
"hint.noScopes": 'sin scopes todavía — `application_scope_create name:"dev"` para tener dónde deployar.',
|
|
370
|
+
"hint.buildNewer": "el build #{id} ({branch} @{commit}) es más nuevo que la release {semver} — `application_deployment_create` lo publica (crea la release por vos).",
|
|
371
|
+
"hint.buildUnreleased": "el build #{id} no tiene release — `application_deployment_create` lo publica (crea la release por vos).",
|
|
372
|
+
"hint.releaseNotLive": "la release {semver} no está en vivo — `application_deployment_create` para publicarla.",
|
|
373
|
+
"hint.noBuilds": "sin builds todavía — pusheá un commit para que CI genere uno, después `application_deployment_create`.",
|
|
374
|
+
"hint.upToDate": "todo está al día ✅",
|
|
375
|
+
"hint.watch": "`application_get deployment:{id}` para seguirlo.",
|
|
376
|
+
"rollout.full": 'todo el tráfico en la versión nueva — `application_deployment_update action:"finalize"` para retirar la vieja.',
|
|
377
|
+
"rollout.moving": "el tráfico va de {from}% → {to}% — `application_get deployment:{id}` para seguirlo.",
|
|
378
|
+
"rollout.at": 'tráfico al {traffic}% — `application_deployment_update percent:<n>` para avanzar (marcas: 1,5,10,25,50,75,90,95,99,100), `application_deployment_update action:"rollback"` para volver atrás.',
|
|
379
|
+
"rollout.instances": "las instancias están levantando — `application_get deployment:{id}` para seguirlo.",
|
|
380
|
+
"rollout.switching": "el tráfico se está moviendo — `application_get deployment:{id}` para seguirlo.",
|
|
381
|
+
"rollout.finalizing": "finalizando — se están retirando las instancias viejas.",
|
|
382
|
+
"rollout.done": "listo — la release está completamente en vivo ✅",
|
|
383
|
+
"rollout.approval": "esperando una aprobación ✋ — alguien tiene que aprobar este deployment (dashboard → Approvals).",
|
|
384
|
+
"rollout.failed": 'el deployment falló — `application_log_list` para ver la salida, `application_deployment_update action:"rollback"` si se movió tráfico.',
|
|
385
|
+
"rollout.rolledBack": "revertido — la versión anterior está sirviendo todo el tráfico.",
|
|
386
|
+
"status.errorLabel": "No pude cargar el estado",
|
|
387
|
+
"findApps.noneMatching": "No hay aplicaciones{matching}{inNamespace}.",
|
|
388
|
+
"findApps.matching": ' que coincidan con "{query}"',
|
|
389
|
+
"findApps.inNamespace": ' en namespaces ~"{namespace}"',
|
|
390
|
+
"findApps.createHint": "`application_create` vincula este repo como una aplicación nueva.",
|
|
391
|
+
"findApps.count.one": "1 aplicación",
|
|
392
|
+
"findApps.count.many": "{count} aplicaciones",
|
|
393
|
+
"findApps.statusHint": '`application_get app:"<nombre>"` para ver el panorama completo de una.',
|
|
394
|
+
"findApps.truncated": "Mostrando las primeras {shown} — afiná con una búsqueda o namespace para ver el resto.",
|
|
395
|
+
"findApps.errorLabel": "Falló la búsqueda",
|
|
396
|
+
"params.unavailable": "El listado de parámetros no está disponible acá. `application_parameter_create` igual funciona.",
|
|
397
|
+
"params.viewInDashboard": "ver en el dashboard",
|
|
398
|
+
"params.none": "**{app}** no tiene parámetros.",
|
|
399
|
+
"params.noneHint": "`application_parameter_create` para agregar variables de entorno o archivos.",
|
|
400
|
+
"params.count.one": "**{app}** · 1 parámetro",
|
|
401
|
+
"params.count.many": "**{app}** · {count} parámetros",
|
|
402
|
+
"params.unset": "_sin valor_",
|
|
403
|
+
"params.applyHint": "los valores cambiados aplican en el próximo `application_deployment_create`.",
|
|
404
|
+
"params.errorLabel": "No pude listar los parámetros",
|
|
405
|
+
"logs.noun": "Los logs",
|
|
406
|
+
"logs.noScopes": "**{app}** no tiene scopes, así que todavía no hay logs de runtime.",
|
|
407
|
+
"logs.noScopesHint": "`application_deployment_create` después de crear un scope — los logs aparecen cuando algo corre.",
|
|
408
|
+
"logs.empty": "No hay líneas de log para **{app}**{scope}.",
|
|
409
|
+
"logs.openInDashboard": "abrir logs en el dashboard",
|
|
410
|
+
"logs.lastLines": "**{app}**{scope} — últimas {count} líneas",
|
|
411
|
+
"logs.errorLabel": "No pude leer los logs",
|
|
412
|
+
"logs.errorSuffix": "La vista Logs del dashboard puede tenerlos igual.",
|
|
413
|
+
"metrics.noun": "Las métricas",
|
|
414
|
+
"metrics.noScopes": "**{app}** no tiene scopes, así que todavía no hay métricas de runtime.",
|
|
415
|
+
"metrics.noScopesHint": "`application_deployment_create` después de crear un scope.",
|
|
416
|
+
"metrics.empty": "No hay datapoints de métricas para **{app}** · {scope} en las últimas {window} — el scope puede ser muy nuevo o el agente de métricas no estar conectado.",
|
|
417
|
+
"metrics.openInDashboard": "performance en el dashboard",
|
|
418
|
+
"metrics.title": "**{app}** · {scope} — últimas {window}",
|
|
419
|
+
"metrics.errorLabel": "No pude cargar las métricas",
|
|
420
|
+
// — application_build_list tool —
|
|
421
|
+
"builds.title": "**{app}** · {count} build(s) reciente(s)",
|
|
422
|
+
"builds.branch": "Branch",
|
|
423
|
+
"builds.commit": "Commit",
|
|
424
|
+
"builds.released": "Released",
|
|
425
|
+
"builds.none": "**{app}** todavía no tiene builds.",
|
|
426
|
+
"builds.noneHint": "pusheá un commit para que CI genere uno, después `application_deployment_create`.",
|
|
427
|
+
"builds.deployHint": "`application_deployment_create build_id:{build}` lo publica (crea la release por vos).",
|
|
428
|
+
"builds.waiting": "el build #{build} sigue corriendo — `application_build_list` de nuevo en un momento para chequear.",
|
|
429
|
+
"builds.upToDate": "el último build exitoso ya está publicado.",
|
|
430
|
+
"builds.assetsTitle": "Assets del build #{build}",
|
|
431
|
+
"builds.noAssets": "El build #{build} no tiene assets (puede estar compilando todavía).",
|
|
432
|
+
"builds.errorLabel": "No pude listar los builds",
|
|
433
|
+
// — organization_get tool —
|
|
434
|
+
"overview.title": "Panorama de la organización · {count} aplicación(es) escaneada(s)",
|
|
435
|
+
"overview.truncated": '_(muestro las primeras {count} — acotá con `organization_get query:"..."`)_',
|
|
436
|
+
"overview.activeRollouts": "Rollouts activos ({count})",
|
|
437
|
+
"overview.trouble": "Requiere atención ({count})",
|
|
438
|
+
"overview.hintActive": '`application_get app:"{app}"` para seguir un rollout, o `application_deployment_update` para manejarlo.',
|
|
439
|
+
"overview.hintTrouble": '`application_log_list app:"{app}"` para investigar, `application_deployment_update action:"rollback"` si hace falta.',
|
|
440
|
+
"overview.hintCalm": "todo tranquilo — nada desplegándose, nada falló.",
|
|
441
|
+
"overview.errorLabel": "No pude armar el panorama",
|
|
442
|
+
// — application_approval_list tool —
|
|
443
|
+
"approvals.title": "**{app}** · {count} aprobación(es)",
|
|
444
|
+
"approvals.action": "Acción",
|
|
445
|
+
"approvals.entity": "Entidad",
|
|
446
|
+
"approvals.requestedBy": "Solicitada por",
|
|
447
|
+
"approvals.none": "No hay aprobaciones pendientes para **{app}**.",
|
|
448
|
+
"approvals.needId": "Pasá `approval_id` junto con la acción.",
|
|
449
|
+
"approvals.invalidId": "Eso no parece un approval id válido — copialo de la lista de `application_approval_list`.",
|
|
450
|
+
"approvals.approved": "Aprobé {id} — la acción bloqueada puede continuar.",
|
|
451
|
+
"approvals.cancelled": "Cancelé la aprobación {id}.",
|
|
452
|
+
"approvals.actHint": '`application_approval_list action:"approve" approval_id:"{id}"` para dejarla continuar (usa tus permisos).',
|
|
453
|
+
"approvals.errorLabel": "No pude cargar las aprobaciones",
|
|
454
|
+
// — application_service_list tool —
|
|
455
|
+
"services.attached": "**{app}** · {count} servicio(s) de dependencia",
|
|
456
|
+
"services.specification": "Tipo",
|
|
457
|
+
"services.none": "**{app}** no tiene servicios de dependencia conectados.",
|
|
458
|
+
"services.catalog": "Disponibles para aprovisionar: {catalog}.",
|
|
459
|
+
"services.provisionHint": "aprovisionar una dependencia es un flujo guiado — creala en el dashboard.",
|
|
460
|
+
"services.errorLabel": "No pude cargar los servicios",
|
|
461
|
+
"deploy.noScopeTypes": "Esta app no tiene scopes todavía. Tipos de scope disponibles: {types}.",
|
|
462
|
+
"deploy.createScopeHint": '`application_scope_create name:"dev" type:"{type}"`',
|
|
463
|
+
"deploy.usingRelease": "Usando la release existente **{semver}** (#{id}). ",
|
|
464
|
+
"deploy.createdRelease": "Creé la release **{semver}** desde el build #{build}. ",
|
|
465
|
+
"deploy.createdReleaseFrom": "Creé la release **{semver}** desde el build #{build} ({branch} @{commit}). ",
|
|
466
|
+
"deploy.noSuchVersion": "No existe la release **{version}**{known} y no hay un build exitoso del cual cortarla.",
|
|
467
|
+
"deploy.knownVersions": " (conocidas: {versions})",
|
|
468
|
+
"deploy.nothing": "Nada para deployar: no hay releases activas ni builds exitosos.",
|
|
469
|
+
"deploy.nothingHint": "pusheá un commit para que CI genere un build y después `application_deployment_create` de nuevo.",
|
|
470
|
+
"deploy.multiAsset": "El build #{build} tiene varios assets — ¿cuál va a **{scope}** ({type})?",
|
|
471
|
+
"deploy.assetHint": '`application_deployment_create asset:"{name}"`',
|
|
472
|
+
"deploy.errorLabel": "Falló el deploy",
|
|
473
|
+
"deploy.alreadyRolling": "Esta release ya se está desplegando en **{scope}** — muestro ese deployment.",
|
|
474
|
+
"deploy.rejected": 'Deploy rechazado: {message}\nEsto suele ser la elección de asset, no los ids — el build probablemente tiene varios assets. Reintentá con `application_deployment_create asset:"<nombre>"` (los ves con `application_get`).',
|
|
475
|
+
"traffic.sayWhat": 'Decime qué hacer: `percent:<0-100>`, `action:"finalize"` o `action:"rollback"`.',
|
|
476
|
+
"traffic.noActive": "No hay rollout activo en **{app}** — `application_deployment_create` arranca uno.",
|
|
477
|
+
"traffic.several": "Hay varios rollouts activos — pasá deployment_id:",
|
|
478
|
+
"traffic.snapped": "({requested}% ajustado a {snapped}%)",
|
|
479
|
+
"traffic.errorLabel": "Falló la acción de tráfico",
|
|
480
|
+
"createRelease.noBuilds": "No hay builds exitosos en **{app}** — pusheá un commit para que CI genere uno.",
|
|
481
|
+
"createRelease.done": "Release **{semver}** creada{from}.",
|
|
482
|
+
"createRelease.exists": "La release **{semver}** ya existe para el build #{build} — la reuso.",
|
|
483
|
+
"createRelease.from": " desde el build #{build} ({branch} @{commit}, {when})",
|
|
484
|
+
"createRelease.deployHint": "`application_deployment_create release_id:{id}` para publicarla.",
|
|
485
|
+
"createRelease.errorLabel": "No pude crear la release",
|
|
486
|
+
"setParams.created": "Configuré {count} parámetro(s) nuevo(s) en **{app}**: {names}.",
|
|
487
|
+
"setParams.updated": "Actualicé {count} parámetro(s) existente(s) en **{app}**: {names}.",
|
|
488
|
+
"setParams.mixed": "Configuré {created} nuevo(s) y actualicé {updated} parámetro(s) en **{app}**: {names}.",
|
|
489
|
+
"setParams.applyHint": "los valores aplican en el próximo `application_deployment_create`.",
|
|
490
|
+
"setParams.partial": "La actualización de parámetros falló a mitad de camino: {message}\nLos parámetros ya creados quedan — `application_parameter_list` para inspeccionar.",
|
|
491
|
+
"setParams.errorLabel": "Falló la actualización de parámetros",
|
|
492
|
+
"createApp.form": "Para crear una aplicación necesito la URL de un repo git y un nombre (el formulario los pide).\nNamespaces: {namespaces}.",
|
|
493
|
+
"createApp.noName": "No pude derivar un nombre desde {url} — pasá name.",
|
|
494
|
+
"createApp.noNamespaceMatch": 'Ningún namespace coincide con "{namespace}". Namespaces: {names}.',
|
|
495
|
+
"createApp.whichNamespace": "¿Qué namespace?",
|
|
496
|
+
"createApp.whichNamespaceFor": "¿Qué namespace debería ser dueño de **{name}**?",
|
|
497
|
+
"createApp.namespaceHint": '`application_create namespace:"<nombre>"`',
|
|
498
|
+
"createApp.noNamespaces": "Esta organización no tiene namespaces — creá uno en el dashboard primero.",
|
|
499
|
+
"createApp.created": "**{name}** creada (#{id}) en el namespace **{namespace}**, vinculada a {repo}.",
|
|
500
|
+
"createApp.alreadyLinked": "**{name}** (#{id}) ya está vinculada a {repo} — no hay nada que crear.",
|
|
501
|
+
"createApp.createdHint": "pusheá un commit para disparar el primer build y después `application_deployment_create`.",
|
|
502
|
+
"createApp.pending": "⏳ **{name}** creada (#{id}), el aprovisionamiento sigue corriendo (estado: {status}).",
|
|
503
|
+
"createApp.pendingHint": '`application_get app:"#{id}"` para revisarla.',
|
|
504
|
+
"createApp.errorLabel": "No pude crear la aplicación",
|
|
505
|
+
// — playbook_get tool —
|
|
506
|
+
"playbook.errorLabel": "No pude cargar el playbook",
|
|
507
|
+
"createScope.whichType": "¿Qué tipo de scope para **{name}**?",
|
|
508
|
+
"createScope.typeHint": '`application_scope_create name:"{name}" type:"<tipo>"`',
|
|
509
|
+
"createScope.noTypes": 'No hay tipos de scope disponibles — pasá type explícito (p. ej. type:"web_pool").',
|
|
510
|
+
"createScope.provisioning": "⏳ El scope **{name}** (#{id}, {type}{dimensions}) se está aprovisionando — se está creando infraestructura.",
|
|
511
|
+
"createScope.exists": "El scope **{name}** (#{id}) ya existe ({status}) — no hay nada que crear.",
|
|
512
|
+
"createScope.provisioningHint": '`application_get` lo muestra; cuando esté activo, `application_deployment_create scope:"{name}"`.',
|
|
513
|
+
"createScope.errorLabel": "No pude crear el scope",
|
|
514
|
+
"error.credentialRejected": "nullplatform rechazó la credencial ({status}) — revisá tu NP_API_KEY",
|
|
515
|
+
"error.permission": "tu usuario de nullplatform no tiene permisos para esta acción (403)",
|
|
516
|
+
"error.bearerNoOrg": "el bearer no es un token emitido por nullplatform (sin claim de organización)",
|
|
517
|
+
"http.authRequired": "autenticación requerida",
|
|
518
|
+
"http.authHint": "mandá tu propia credencial de nullplatform: `Authorization: Bearer <NP_API_KEY>` (o X-NP-API-Key)",
|
|
519
|
+
"http.originDenied": "origin no permitido",
|
|
520
|
+
"http.bodyTooLarge": "el cuerpo del request es demasiado grande",
|
|
521
|
+
"http.invalidJson": "cuerpo JSON inválido",
|
|
522
|
+
"http.verifyUnavailable": "no se pudo verificar la credencial con nullplatform",
|
|
523
|
+
"http.rateLimited": "demasiadas solicitudes — bajá el ritmo",
|
|
524
|
+
"prompt.ship": `Publicá el último código de {app}{scope}.
|
|
525
|
+
1. Corré status primero y decime qué se va a publicar (build/release).
|
|
526
|
+
2. Hacé deploy.
|
|
527
|
+
3. Subí el tráfico por pasos — 25%, después 50%, después 100% — revisando status entre pasos; frená y avisame si algo se ve mal.
|
|
528
|
+
4. Cuando el 100% esté sano, finalizá y confirmá con un status final.`,
|
|
529
|
+
"prompt.ship.toScope": ' al scope "{scope}"',
|
|
530
|
+
"prompt.thisRepoApp": "la aplicación vinculada a este repo",
|
|
531
|
+
"prompt.setup": `Conectá este repositorio a nullplatform:
|
|
532
|
+
1. Corré application_get — si el repo ya está vinculado a una aplicación, mostrame dónde estoy parado.
|
|
533
|
+
2. Si no está vinculado, buscá con application_list una app que coincida con el nombre del repo antes de crear nada.
|
|
534
|
+
3. Si no existe ninguna, application_create para vincularlo; después mostrá application_get y decime qué sigue (primer build vía CI, después deploy).`,
|
|
535
|
+
"prompt.health": `Dame un chequeo de salud de {app}:
|
|
536
|
+
1. status — ¿qué está en vivo y dónde? ¿algo a mitad de rollout?
|
|
537
|
+
2. metrics (ventana de 3h) — marcá cualquier cosa rara en throughput, tiempo de respuesta, tasa de errores, CPU o memoria.
|
|
538
|
+
3. logs — escaneá las líneas recientes buscando errores o warnings.
|
|
539
|
+
Cerrá con un veredicto de un párrafo: sano / degradado / en problemas, y la próxima acción más útil.`,
|
|
540
|
+
"prompt.rollback": `Algo está mal — revertí {app} ya mismo: encontrá su rollout activo, corré application_deployment_update action:"rollback" y confirmá con application_get que la versión anterior está sirviendo tráfico; resumí qué se revirtió.`,
|
|
541
|
+
};
|
|
542
|
+
const catalogs = { en: english, es: spanish };
|
|
543
|
+
const SUPPORTED = ["en", "es"];
|
|
544
|
+
let defaultLocale = "en";
|
|
545
|
+
const localeScope = new AsyncLocalStorage();
|
|
546
|
+
/** Process-wide default (stdio: from NP_LANG/LANG at startup). */
|
|
547
|
+
export function setDefaultLocale(locale) {
|
|
548
|
+
defaultLocale = locale;
|
|
549
|
+
}
|
|
550
|
+
/** Run `work` with a request-scoped locale (HTTP: from Accept-Language). */
|
|
551
|
+
export function withLocale(locale, work) {
|
|
552
|
+
return localeScope.run(locale, work);
|
|
553
|
+
}
|
|
554
|
+
export function currentLocale() {
|
|
555
|
+
return localeScope.getStore() ?? defaultLocale;
|
|
556
|
+
}
|
|
557
|
+
/** English/native language names the model might pass instead of an ISO code. */
|
|
558
|
+
const LANGUAGE_NAMES = {
|
|
559
|
+
english: "en",
|
|
560
|
+
inglés: "en",
|
|
561
|
+
ingles: "en",
|
|
562
|
+
spanish: "es",
|
|
563
|
+
español: "es",
|
|
564
|
+
espanol: "es",
|
|
565
|
+
castellano: "es",
|
|
566
|
+
};
|
|
567
|
+
/** "es-AR, en;q=0.9" / "es_AR.UTF-8" / "es" / "Spanish" -> a supported locale, or undefined. */
|
|
568
|
+
export function matchLocale(input) {
|
|
569
|
+
for (const candidate of (input ?? "").split(",")) {
|
|
570
|
+
const token = candidate.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
571
|
+
if (!token)
|
|
572
|
+
continue;
|
|
573
|
+
const byName = LANGUAGE_NAMES[token];
|
|
574
|
+
if (byName)
|
|
575
|
+
return byName;
|
|
576
|
+
const code = token.slice(0, 2);
|
|
577
|
+
const supported = SUPPORTED.find((locale) => locale === code);
|
|
578
|
+
if (supported)
|
|
579
|
+
return supported;
|
|
580
|
+
}
|
|
581
|
+
return undefined;
|
|
582
|
+
}
|
|
583
|
+
/** Like matchLocale, defaulting to en — for transport/env fallbacks. */
|
|
584
|
+
export function resolveLocale(input) {
|
|
585
|
+
return matchLocale(input) ?? "en";
|
|
586
|
+
}
|
|
587
|
+
/** Translate a message, interpolating `{name}` placeholders. */
|
|
588
|
+
export function translate(key, params) {
|
|
589
|
+
const template = catalogs[currentLocale()][key];
|
|
590
|
+
return template.replace(/\{(\w+)\}/g, (placeholder, name) => params?.[name] !== undefined ? String(params[name]) : placeholder);
|
|
591
|
+
}
|
|
592
|
+
/** One/many message pairs — the closest thing to plural rules this catalog needs. */
|
|
593
|
+
export function plural(count, oneKey, manyKey, params) {
|
|
594
|
+
return translate(count === 1 ? oneKey : manyKey, { count, ...params });
|
|
595
|
+
}
|