@allowance/cli 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 +212 -0
- package/bin/allowance.js +35 -0
- package/lib/runtime.js +181 -0
- package/package.json +36 -0
- package/scripts/npm-postinstall.js +21 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Allowance
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# allowance
|
|
2
|
+
|
|
3
|
+
`allowance` is a human-first CLI for Allowance purchase workflows.
|
|
4
|
+
|
|
5
|
+
It uses an `ak_...` connection token at runtime and stores credentials in the OS keychain.
|
|
6
|
+
Runtime actions are token-scoped, so requests/allowances are visible to the token session that created them.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
Primary (npm):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g @allowance/cli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This npm package exposes the `allowance` command and bootstraps the Python runtime automatically.
|
|
17
|
+
Prerequisite: Python 3.12+ on PATH (`python3` or `python`).
|
|
18
|
+
|
|
19
|
+
Secondary (pipx):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pipx install allowance
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Local source install:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pipx install .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Local development:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv sync --dev
|
|
35
|
+
uv run allowance --help
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Publish to npm
|
|
39
|
+
|
|
40
|
+
This repo supports npm publishing through GitHub Actions.
|
|
41
|
+
|
|
42
|
+
### One-time setup
|
|
43
|
+
|
|
44
|
+
1. Create the npm scope/org `@allowance` and grant maintainer access.
|
|
45
|
+
2. Create an npm automation token with publish permission.
|
|
46
|
+
3. Add it as a GitHub repository secret:
|
|
47
|
+
- name: `NPM_TOKEN`
|
|
48
|
+
- value: npm token string
|
|
49
|
+
|
|
50
|
+
### Release flow
|
|
51
|
+
|
|
52
|
+
1. Bump `version` in `package.json`.
|
|
53
|
+
2. Commit and push `main`.
|
|
54
|
+
3. Tag and push a npm release tag:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git tag npm-v0.1.1
|
|
58
|
+
git push origin npm-v0.1.1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
4. GitHub Action `.github/workflows/publish-npm.yml` runs and publishes `@allowance/cli`.
|
|
62
|
+
|
|
63
|
+
After workflow success, users can install with:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install -g @allowance/cli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Publish to PyPI
|
|
70
|
+
|
|
71
|
+
This repo also supports PyPI publishing for `pipx` users.
|
|
72
|
+
|
|
73
|
+
### One-time setup
|
|
74
|
+
|
|
75
|
+
1. Create a PyPI account for the maintainer/org.
|
|
76
|
+
2. Create a project-scoped API token in PyPI.
|
|
77
|
+
3. Add the token as a GitHub repository secret:
|
|
78
|
+
- name: `PYPI_API_TOKEN`
|
|
79
|
+
- value: `pypi-...`
|
|
80
|
+
|
|
81
|
+
### Release flow
|
|
82
|
+
|
|
83
|
+
1. Bump `version` in `pyproject.toml`.
|
|
84
|
+
2. Commit and push `main`.
|
|
85
|
+
3. Tag and push a version tag:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git tag v0.1.1
|
|
89
|
+
git push origin v0.1.1
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
4. GitHub Action `.github/workflows/publish-pypi.yml` runs:
|
|
93
|
+
- lint/type/test
|
|
94
|
+
- build wheels/sdist
|
|
95
|
+
- publish to PyPI
|
|
96
|
+
|
|
97
|
+
## Fast Purchase Path (Recommended)
|
|
98
|
+
|
|
99
|
+
Use this flow for fastest agent and customer experience:
|
|
100
|
+
|
|
101
|
+
1. Authenticate once for the session.
|
|
102
|
+
2. Create a request with clear merchant + reason + cap amount.
|
|
103
|
+
3. Ask customer to approve immediately in app.
|
|
104
|
+
4. Poll until approved.
|
|
105
|
+
5. Issue card only when checkout is ready.
|
|
106
|
+
6. Complete checkout.
|
|
107
|
+
7. Report purchase outcome immediately (`success` or `failed`).
|
|
108
|
+
8. If failed and still active, re-issue and retry within policy limits.
|
|
109
|
+
|
|
110
|
+
Example:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
allowance auth login --email you@example.com
|
|
114
|
+
allowance requests create --cents 5000 --merchant "Ticketmaster" --reason "2 tickets under $50"
|
|
115
|
+
allowance requests poll <request_id>
|
|
116
|
+
allowance cards issue <allowance_id> --expected-cents 4500
|
|
117
|
+
allowance cards report <allowance_id> <attempt_id> --status success --charged-cents 4380
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Auth
|
|
121
|
+
|
|
122
|
+
Login bootstraps via OTP, then mints and stores a connection token:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
allowance auth login --email you@example.com
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
If already logged in, login exits with:
|
|
129
|
+
|
|
130
|
+
`Already logged in as <email>. Run allowance auth logout to switch accounts.`
|
|
131
|
+
|
|
132
|
+
Status command:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
allowance auth status
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Logout command (revokes remote token and clears keychain):
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
allowance auth logout
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Command Reference
|
|
145
|
+
|
|
146
|
+
All commands output JSON by default. Use `--pretty` for formatted JSON.
|
|
147
|
+
|
|
148
|
+
| Command | What It Does | Use When |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `allowance auth login --email <email>` | Signs in via OTP bootstrap and stores token in keychain | Starting a session |
|
|
151
|
+
| `allowance auth status` | Shows whether CLI is logged in and which account/token prefix | Preflight checks |
|
|
152
|
+
| `allowance auth logout` | Revokes token and clears local credentials | End of session/security cleanup |
|
|
153
|
+
| `allowance requests create --cents ... --merchant ... --reason ... [--currency] [--expires-at]` | Creates a pending allowance request | You need user approval before spending |
|
|
154
|
+
| `allowance requests list` | Lists requests created by this token session | Find recent request IDs |
|
|
155
|
+
| `allowance requests get <request_id>` | Fetches full request detail | Inspect request state/details |
|
|
156
|
+
| `allowance requests poll <request_id>` | Returns current request status and `allowance_id` when approved | Waiting on user approval |
|
|
157
|
+
| `allowance requests cancel <request_id>` | Cancels a still-pending request | User changed mind / no longer needed |
|
|
158
|
+
| `allowance cards issue <allowance_id> --expected-cents <int>` | Issues checkout card for one purchase attempt | Approval exists and checkout is ready |
|
|
159
|
+
| `allowance cards attempts <allowance_id>` | Lists purchase attempts for that allowance | Audit/retry decisions |
|
|
160
|
+
| `allowance cards report <allowance_id> <attempt_id> --status success\\|failed ...` | Finalizes attempt result and updates allowance state | Immediately after checkout attempt |
|
|
161
|
+
| `allowance allowances list` | Lists allowances visible to current token | Find/manage active allowances |
|
|
162
|
+
| `allowance allowances get <allowance_id>` | Gets one allowance snapshot | Verify state/retry budget |
|
|
163
|
+
| `allowance allowances pause <allowance_id>` | Pauses spending on allowance | Temporary stop needed |
|
|
164
|
+
| `allowance allowances revoke <allowance_id>` | Permanently revokes allowance | Final stop / cleanup |
|
|
165
|
+
|
|
166
|
+
`allowance allowances unpause` is intentionally not exposed in this CLI.
|
|
167
|
+
|
|
168
|
+
## Agent + Customer Experience Guidelines
|
|
169
|
+
|
|
170
|
+
1. Request only when purchase intent and budget are clear.
|
|
171
|
+
2. Use merchant/reason text the customer can quickly recognize in approval UI.
|
|
172
|
+
3. After `requests create`, immediately provide request summary and ask customer to approve.
|
|
173
|
+
4. Poll with short intervals, but keep customer informed instead of silently looping.
|
|
174
|
+
5. Issue card only at the final checkout step to reduce stale credential windows.
|
|
175
|
+
6. Always call `cards report` right after checkout outcome.
|
|
176
|
+
7. Never echo PAN/CVV unless customer explicitly asks.
|
|
177
|
+
|
|
178
|
+
## Error Handling
|
|
179
|
+
|
|
180
|
+
| Status | Meaning | Typical Action |
|
|
181
|
+
|---|---|---|
|
|
182
|
+
| `401` | Auth invalid/expired | Re-login |
|
|
183
|
+
| `403` | Not allowed for this actor | Use correct actor/flow |
|
|
184
|
+
| `404` | Not found or out-of-scope token access | Verify IDs and token session |
|
|
185
|
+
| `409` | State conflict (not active, retry exhausted, etc.) | Inspect current resource state and adjust |
|
|
186
|
+
| `503` | Upstream issuer/provider unavailable | Retry later or fail gracefully |
|
|
187
|
+
|
|
188
|
+
## Global Flags
|
|
189
|
+
|
|
190
|
+
- `--api-base-url` to target another environment for one invocation
|
|
191
|
+
- `--pretty` for formatted JSON output
|
|
192
|
+
|
|
193
|
+
Example:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
allowance --api-base-url http://127.0.0.1:8000 --pretty allowances list
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Configuration
|
|
200
|
+
|
|
201
|
+
- `ALLOWANCE_API_BASE_URL` (default: `https://allowance-api.fly.dev`)
|
|
202
|
+
- `ALLOWANCE_HTTP_TIMEOUT_SECONDS` (default: `20`)
|
|
203
|
+
- `ALLOWANCE_CLI_KEYCHAIN_SERVICE` (optional keychain namespace override)
|
|
204
|
+
- `ALLOWANCE_CLI_KEYCHAIN_ACCOUNT` (optional keychain account override)
|
|
205
|
+
|
|
206
|
+
## Tests
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
./scripts/test_unit.sh
|
|
210
|
+
./scripts/test_smoke.sh
|
|
211
|
+
./scripts/test_all.sh
|
|
212
|
+
```
|
package/bin/allowance.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const childProcess = require('node:child_process');
|
|
5
|
+
|
|
6
|
+
const packageJson = require('../package.json');
|
|
7
|
+
const { allowanceExecutablePath, ensureRuntime } = require('../lib/runtime');
|
|
8
|
+
|
|
9
|
+
function main() {
|
|
10
|
+
const pypiPackage = process.env.ALLOWANCE_PYPI_PACKAGE || 'allowance';
|
|
11
|
+
ensureRuntime({
|
|
12
|
+
version: packageJson.version,
|
|
13
|
+
pypiPackage,
|
|
14
|
+
skipInstall: false,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const result = childProcess.spawnSync(allowanceExecutablePath(), process.argv.slice(2), {
|
|
18
|
+
stdio: 'inherit',
|
|
19
|
+
env: process.env,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (result.error) {
|
|
23
|
+
throw result.error;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
main();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33
|
+
console.error(`[allowance] ${message}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
package/lib/runtime.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const childProcess = require('node:child_process');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
const PYTHON_MIN_MAJOR = 3;
|
|
9
|
+
const PYTHON_MIN_MINOR = 12;
|
|
10
|
+
|
|
11
|
+
function runtimeRoot() {
|
|
12
|
+
return process.env.ALLOWANCE_NPM_RUNTIME_DIR || path.join(os.homedir(), '.allowance-cli-runtime');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function venvDir() {
|
|
16
|
+
return path.join(runtimeRoot(), 'venv');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function stateFilePath() {
|
|
20
|
+
return path.join(runtimeRoot(), 'state.json');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function venvPythonPath() {
|
|
24
|
+
if (process.platform === 'win32') {
|
|
25
|
+
return path.join(venvDir(), 'Scripts', 'python.exe');
|
|
26
|
+
}
|
|
27
|
+
return path.join(venvDir(), 'bin', 'python');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function allowanceExecutablePath() {
|
|
31
|
+
if (process.platform === 'win32') {
|
|
32
|
+
return path.join(venvDir(), 'Scripts', 'allowance.exe');
|
|
33
|
+
}
|
|
34
|
+
return path.join(venvDir(), 'bin', 'allowance');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseVersion(versionText) {
|
|
38
|
+
const match = versionText.match(/(\d+)\.(\d+)/);
|
|
39
|
+
if (!match) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
major: Number(match[1]),
|
|
44
|
+
minor: Number(match[2]),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function pythonVersionSupported(version) {
|
|
49
|
+
if (!version) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (version.major > PYTHON_MIN_MAJOR) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (version.major < PYTHON_MIN_MAJOR) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return version.minor >= PYTHON_MIN_MINOR;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function detectPythonCommand() {
|
|
62
|
+
const candidates = [];
|
|
63
|
+
|
|
64
|
+
if (process.env.ALLOWANCE_PYTHON) {
|
|
65
|
+
candidates.push([process.env.ALLOWANCE_PYTHON]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (process.platform === 'win32') {
|
|
69
|
+
candidates.push(['py', '-3']);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
candidates.push(['python3.13']);
|
|
73
|
+
candidates.push(['python3.12']);
|
|
74
|
+
candidates.push(['python3']);
|
|
75
|
+
candidates.push(['python']);
|
|
76
|
+
|
|
77
|
+
for (const candidate of candidates) {
|
|
78
|
+
const check = childProcess.spawnSync(
|
|
79
|
+
candidate[0],
|
|
80
|
+
candidate.slice(1).concat(['-c', 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")']),
|
|
81
|
+
{ encoding: 'utf8' },
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (check.error || check.status !== 0) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const parsed = parseVersion((check.stdout || '').trim());
|
|
89
|
+
if (pythonVersionSupported(parsed)) {
|
|
90
|
+
return candidate;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function runOrThrow(command, args, options = {}) {
|
|
98
|
+
const result = childProcess.spawnSync(command, args, {
|
|
99
|
+
stdio: options.quiet ? 'pipe' : 'inherit',
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
});
|
|
102
|
+
if (result.error) {
|
|
103
|
+
throw result.error;
|
|
104
|
+
}
|
|
105
|
+
if (result.status !== 0) {
|
|
106
|
+
if (options.quiet) {
|
|
107
|
+
if (result.stdout) {
|
|
108
|
+
process.stderr.write(result.stdout);
|
|
109
|
+
}
|
|
110
|
+
if (result.stderr) {
|
|
111
|
+
process.stderr.write(result.stderr);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw new Error(`Command failed: ${command} ${args.join(' ')}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function readState() {
|
|
119
|
+
try {
|
|
120
|
+
return JSON.parse(fs.readFileSync(stateFilePath(), 'utf8'));
|
|
121
|
+
} catch (_error) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function writeState(state) {
|
|
127
|
+
fs.mkdirSync(runtimeRoot(), { recursive: true });
|
|
128
|
+
fs.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function ensureVenv(pythonCommand) {
|
|
132
|
+
const pythonPath = venvPythonPath();
|
|
133
|
+
if (fs.existsSync(pythonPath)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fs.mkdirSync(runtimeRoot(), { recursive: true });
|
|
138
|
+
runOrThrow(pythonCommand[0], pythonCommand.slice(1).concat(['-m', 'venv', venvDir()]));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function installPinnedPackage(targetSpec) {
|
|
142
|
+
const pythonPath = venvPythonPath();
|
|
143
|
+
runOrThrow(pythonPath, ['-m', 'pip', 'install', '--upgrade', 'pip'], { quiet: true });
|
|
144
|
+
runOrThrow(pythonPath, ['-m', 'pip', 'install', targetSpec], { quiet: true });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function ensureRuntime(options) {
|
|
148
|
+
const targetSpec = `${options.pypiPackage}==${options.version}`;
|
|
149
|
+
const currentState = readState();
|
|
150
|
+
|
|
151
|
+
if (fs.existsSync(allowanceExecutablePath()) && currentState && currentState.targetSpec === targetSpec) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const pythonCommand = detectPythonCommand();
|
|
156
|
+
if (!pythonCommand) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
'Python 3.12+ is required. Install a supported Python or set ALLOWANCE_PYTHON.',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
ensureVenv(pythonCommand);
|
|
163
|
+
|
|
164
|
+
if (!options.skipInstall) {
|
|
165
|
+
console.error('[allowance] Bootstrapping runtime (one-time setup)...');
|
|
166
|
+
installPinnedPackage(targetSpec);
|
|
167
|
+
writeState({
|
|
168
|
+
targetSpec,
|
|
169
|
+
installedAt: new Date().toISOString(),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!fs.existsSync(allowanceExecutablePath())) {
|
|
174
|
+
throw new Error('Bootstrap failed: allowance executable was not installed in the runtime venv.');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
allowanceExecutablePath,
|
|
180
|
+
ensureRuntime,
|
|
181
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@allowance/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Allowance CLI wrapper package for npm global installs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/dasmer/allowance-cli.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/dasmer/allowance-cli",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/dasmer/allowance-cli/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"allowance",
|
|
16
|
+
"cli",
|
|
17
|
+
"payments",
|
|
18
|
+
"agents"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"allowance": "bin/allowance.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"lib",
|
|
26
|
+
"scripts/npm-postinstall.js",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"postinstall": "node scripts/npm-postinstall.js"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const packageJson = require('../package.json');
|
|
4
|
+
const { ensureRuntime } = require('../lib/runtime');
|
|
5
|
+
|
|
6
|
+
function main() {
|
|
7
|
+
const pypiPackage = process.env.ALLOWANCE_PYPI_PACKAGE || 'allowance';
|
|
8
|
+
ensureRuntime({
|
|
9
|
+
version: packageJson.version,
|
|
10
|
+
pypiPackage,
|
|
11
|
+
skipInstall: false,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
main();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
+
console.warn(`[allowance] Postinstall bootstrap skipped: ${message}`);
|
|
20
|
+
console.warn('[allowance] The runtime will bootstrap on first command execution.');
|
|
21
|
+
}
|