@1aboveio/skills 1.0.8 → 1.0.9
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/.codex-plugin/plugin.json +13 -0
- package/.codex-plugin/resources.json +1827 -0
- package/README.md +22 -0
- package/package.json +9 -2
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/airflow/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/SKILL.md +1 -1
- package/skills/app-dev/android-foundation/release-check.mjs +3 -1
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/app-debug/release-check.mjs +3 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/better-auth/release-check.mjs +3 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/release-check.mjs +3 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/data-quality/release-check.mjs +3 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/monorepo/release-check.mjs +3 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/pyspark/release-check.mjs +3 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/python-backend/release-check.mjs +3 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/shadcn/release-check.mjs +3 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/release-check.mjs +3 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-build/release-check.mjs +3 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/release-check.mjs +3 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/docker/release-check.mjs +3 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-actions/release-check.mjs +3 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/github-flow/release-check.mjs +3 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/google-cloud/release-check.mjs +3 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/mergify/release-check.mjs +3 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/cicd/podman/release-check.mjs +3 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-review/release-check.mjs +3 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/article-to-image/release-check.mjs +3 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/flowchart-svg/release-check.mjs +3 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/image-gen/release-check.mjs +3 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/mermaid/release-check.mjs +3 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/release-check.mjs +3 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-publisher/release-check.mjs +3 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/wechat-search/release-check.mjs +3 -1
- package/skills/content/xai-search/SKILL.md +88 -13
- package/skills/content/xai-search/release-check.mjs +3 -1
- package/skills/content/xai-search/scripts/xai_search.sh +125 -149
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/distill-writing/release-check.mjs +3 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/doc-to-md/release-check.mjs +3 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/editorial-review/release-check.mjs +3 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/release-check.mjs +3 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/plain-language/release-check.mjs +3 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/polish-cn/release-check.mjs +3 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/reg-parser/release-check.mjs +3 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/release-check.mjs +3 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-doc/release-check.mjs +3 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/better-review/release-check.mjs +3 -1
- package/skills/engineering/contributing/SKILL.md +1 -1
- package/skills/engineering/contributing/release-check.mjs +3 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/e2e-test/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/golden-path/release-check.mjs +3 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/references/preflight.md +7 -0
- package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
- package/skills/engineering/resolve-issues/references/task-plan.md +5 -1
- package/skills/engineering/resolve-issues/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/resolve-release/release-check.mjs +3 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-issues/release-check.mjs +3 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/rush-release/release-check.mjs +3 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/engineering/test-quality/release-check.mjs +3 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
- package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -436
- package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
- package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
- package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
- package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
- package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
- package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
- package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
- package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
- package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
- package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
- package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
- package/skills/content/image-gen/scripts/options.test.mjs +0 -245
- package/skills/content/xai-search/tests/smoke_test.sh +0 -36
- package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
- package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
- package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
- package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
- package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
- package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
- package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
- package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
- package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
- package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
- package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
- package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
- package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
- package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
- package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
- package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
- package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
- package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
- package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
- package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
- package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
- package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
- package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
- package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
- package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
- package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
- package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
- package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
- package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
- package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
- package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
- package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
- package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
- package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
- package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
- package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
- package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
- package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
- package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
- package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
- package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
- package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
- package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
- package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
- package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
- package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
- package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
- package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { mkdtemp, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { describe, it } from 'node:test';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
DEFAULT_OPENAI_BASE_URL,
|
|
9
|
-
expandPiValue,
|
|
10
|
-
piModelsPath,
|
|
11
|
-
resolveGemini,
|
|
12
|
-
resolveOpenAI,
|
|
13
|
-
} from './credentials.mjs';
|
|
14
|
-
|
|
15
|
-
async function writeConfig(contents) {
|
|
16
|
-
const dir = await mkdtemp(join(tmpdir(), 'image-gen-creds-'));
|
|
17
|
-
const path = join(dir, 'models.json');
|
|
18
|
-
await writeFile(path, typeof contents === 'string' ? contents : JSON.stringify(contents));
|
|
19
|
-
return path;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const piConfig = (openai) => ({ providers: { openai } });
|
|
23
|
-
|
|
24
|
-
// A literal key, the case where the pi fallback can actually produce one.
|
|
25
|
-
const LITERAL = { baseUrl: 'https://pi-gateway.example/v1', apiKey: 'pi-key' };
|
|
26
|
-
|
|
27
|
-
describe('expandPiValue', () => {
|
|
28
|
-
it('passes literals through', () => {
|
|
29
|
-
assert.equal(expandPiValue('sk-literal', {}).value, 'sk-literal');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('expands $VAR and ${VAR}', () => {
|
|
33
|
-
assert.equal(expandPiValue('$KEY', { KEY: 'v' }).value, 'v');
|
|
34
|
-
assert.equal(expandPiValue('${KEY}', { KEY: 'v' }).value, 'v');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('expands variables embedded in surrounding text', () => {
|
|
38
|
-
const resolved = expandPiValue('https://$HOST/v1', { HOST: 'gw.example' });
|
|
39
|
-
|
|
40
|
-
assert.equal(resolved.value, 'https://gw.example/v1');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('reports every unresolved variable instead of expanding partially', () => {
|
|
44
|
-
const resolved = expandPiValue('$A-$B', { A: 'a' });
|
|
45
|
-
|
|
46
|
-
assert.equal(resolved.value, undefined);
|
|
47
|
-
assert.deepEqual(resolved.missing, ['B']);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('treats an empty variable as unset, as pi does', () => {
|
|
51
|
-
assert.deepEqual(expandPiValue('$KEY', { KEY: '' }).missing, ['KEY']);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('honors the $$ and $! escapes', () => {
|
|
55
|
-
assert.equal(expandPiValue('$$KEY', { KEY: 'v' }).value, '$KEY');
|
|
56
|
-
assert.equal(expandPiValue('$!cmd', {}).value, '!cmd');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('keeps a dangling $ or unterminated ${ literal', () => {
|
|
60
|
-
assert.equal(expandPiValue('pre$', {}).value, 'pre$');
|
|
61
|
-
assert.equal(expandPiValue('${unterminated', {}).value, '${unterminated');
|
|
62
|
-
assert.equal(expandPiValue('$ {KEY}', {}).value, '$ {KEY}');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('flags !command values rather than executing them', () => {
|
|
66
|
-
const resolved = expandPiValue('!security find-generic-password -w', {});
|
|
67
|
-
|
|
68
|
-
assert.equal(resolved.value, undefined);
|
|
69
|
-
assert.equal(resolved.command, 'security find-generic-password -w');
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe('resolveOpenAI', () => {
|
|
74
|
-
it('prefers the environment over the pi config', async () => {
|
|
75
|
-
const configPath = await writeConfig(piConfig(LITERAL));
|
|
76
|
-
const resolved = await resolveOpenAI({
|
|
77
|
-
env: { OPENAI_API_KEY: 'env-key', OPENAI_BASE_URL: 'https://env-gateway.example/v1' },
|
|
78
|
-
configPath,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
assert.equal(resolved.ok, true);
|
|
82
|
-
assert.equal(resolved.apiKey, 'env-key');
|
|
83
|
-
assert.equal(resolved.baseUrl, 'https://env-gateway.example/v1');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('defaults the base URL when only the env key is set', async () => {
|
|
87
|
-
const resolved = await resolveOpenAI({ env: { OPENAI_API_KEY: 'k' }, configPath: '/nope' });
|
|
88
|
-
|
|
89
|
-
assert.equal(resolved.baseUrl, DEFAULT_OPENAI_BASE_URL);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('strips trailing slashes so the path join stays well formed', async () => {
|
|
93
|
-
const resolved = await resolveOpenAI({
|
|
94
|
-
env: { OPENAI_API_KEY: 'k', OPENAI_BASE_URL: 'https://gw.example/v1///' },
|
|
95
|
-
configPath: '/nope',
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
assert.equal(resolved.baseUrl, 'https://gw.example/v1');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('falls back to a literal pi key, taking pi\'s base URL with it', async () => {
|
|
102
|
-
const configPath = await writeConfig(piConfig(LITERAL));
|
|
103
|
-
const resolved = await resolveOpenAI({ env: {}, configPath });
|
|
104
|
-
|
|
105
|
-
assert.equal(resolved.ok, true);
|
|
106
|
-
assert.equal(resolved.apiKey, 'pi-key');
|
|
107
|
-
assert.equal(resolved.baseUrl, 'https://pi-gateway.example/v1');
|
|
108
|
-
assert.match(resolved.source, /^pi config/);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('expands a pi key that references another variable', async () => {
|
|
112
|
-
const configPath = await writeConfig(piConfig({ ...LITERAL, apiKey: '$GAPSEE_KEY' }));
|
|
113
|
-
const resolved = await resolveOpenAI({ env: { GAPSEE_KEY: 'sk-real' }, configPath });
|
|
114
|
-
|
|
115
|
-
assert.equal(resolved.ok, true);
|
|
116
|
-
assert.equal(resolved.apiKey, 'sk-real');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('never sends an unexpanded template as the bearer token', async () => {
|
|
120
|
-
// The bug this guards: `$OPENAI_API_KEY` reaching the API verbatim.
|
|
121
|
-
const configPath = await writeConfig(piConfig({ ...LITERAL, apiKey: '$SOME_KEY' }));
|
|
122
|
-
const resolved = await resolveOpenAI({ env: {}, configPath });
|
|
123
|
-
|
|
124
|
-
assert.equal(resolved.ok, false);
|
|
125
|
-
assert.match(resolved.reason, /\$SOME_KEY/);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('explains why a pi entry pointing at $OPENAI_API_KEY can never satisfy the fallback', async () => {
|
|
129
|
-
const configPath = await writeConfig(piConfig({ ...LITERAL, apiKey: '$OPENAI_API_KEY' }));
|
|
130
|
-
const resolved = await resolveOpenAI({ env: {}, configPath });
|
|
131
|
-
|
|
132
|
-
assert.equal(resolved.ok, false);
|
|
133
|
-
assert.match(resolved.reason, /can never satisfy this fallback/);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('expands a templated pi base URL', async () => {
|
|
137
|
-
const configPath = await writeConfig(piConfig({ apiKey: 'pi-key', baseUrl: 'https://$GW/v1' }));
|
|
138
|
-
const resolved = await resolveOpenAI({ env: { GW: 'gw.example' }, configPath });
|
|
139
|
-
|
|
140
|
-
assert.equal(resolved.baseUrl, 'https://gw.example/v1');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('fails rather than silently retargeting when the pi base URL cannot expand', async () => {
|
|
144
|
-
// Falling back to api.openai.com here would send a gateway key to OpenAI.
|
|
145
|
-
const configPath = await writeConfig(piConfig({ apiKey: 'pi-key', baseUrl: 'https://$GW/v1' }));
|
|
146
|
-
const resolved = await resolveOpenAI({ env: {}, configPath });
|
|
147
|
-
|
|
148
|
-
assert.equal(resolved.ok, false);
|
|
149
|
-
assert.match(resolved.reason, /\$GW/);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('defaults the base URL when the pi provider omits one', async () => {
|
|
153
|
-
const configPath = await writeConfig(piConfig({ apiKey: 'pi-key' }));
|
|
154
|
-
const resolved = await resolveOpenAI({ env: {}, configPath });
|
|
155
|
-
|
|
156
|
-
assert.equal(resolved.baseUrl, DEFAULT_OPENAI_BASE_URL);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('reports !command keys as unsupported instead of running them', async () => {
|
|
160
|
-
const configPath = await writeConfig(piConfig({ ...LITERAL, apiKey: '!echo sk-x' }));
|
|
161
|
-
const resolved = await resolveOpenAI({ env: {}, configPath });
|
|
162
|
-
|
|
163
|
-
assert.equal(resolved.ok, false);
|
|
164
|
-
assert.match(resolved.reason, /does not execute config commands/);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('ignores OPENAI_BASE_URL when the key comes from pi', async () => {
|
|
168
|
-
const configPath = await writeConfig(piConfig(LITERAL));
|
|
169
|
-
const resolved = await resolveOpenAI({
|
|
170
|
-
env: { OPENAI_BASE_URL: 'https://env-gateway.example/v1' },
|
|
171
|
-
configPath,
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
assert.equal(resolved.baseUrl, 'https://pi-gateway.example/v1');
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('fails with the config path when there is nothing to fall back to', async () => {
|
|
178
|
-
const missing = await resolveOpenAI({ env: {}, configPath: '/definitely/not/here.json' });
|
|
179
|
-
const malformed = await resolveOpenAI({ env: {}, configPath: await writeConfig('{ not json') });
|
|
180
|
-
const noKey = await writeConfig(piConfig({ baseUrl: 'https://x.example/v1' }));
|
|
181
|
-
const otherProvider = await writeConfig({ providers: { deepseek: { apiKey: 'k' } } });
|
|
182
|
-
|
|
183
|
-
assert.equal(missing.ok, false);
|
|
184
|
-
assert.match(missing.reason, /definitely\/not\/here\.json/);
|
|
185
|
-
assert.equal(malformed.ok, false);
|
|
186
|
-
assert.equal((await resolveOpenAI({ env: {}, configPath: noKey })).ok, false);
|
|
187
|
-
assert.equal((await resolveOpenAI({ env: {}, configPath: otherProvider })).ok, false);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
describe('piModelsPath', () => {
|
|
192
|
-
it('honors PI_CODING_AGENT_DIR', () => {
|
|
193
|
-
assert.equal(piModelsPath({ PI_CODING_AGENT_DIR: '/custom/agent' }), '/custom/agent/models.json');
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('defaults to the pi agent directory under $HOME', () => {
|
|
197
|
-
assert.match(piModelsPath({}), /[/\\]\.pi[/\\]agent[/\\]models\.json$/);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe('resolveGemini', () => {
|
|
202
|
-
it('reads the env key', () => {
|
|
203
|
-
assert.equal(resolveGemini({ env: { GEMINI_API_KEY: 'g-key' } }).apiKey, 'g-key');
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('fails with a reason without one', () => {
|
|
207
|
-
const resolved = resolveGemini({ env: {} });
|
|
208
|
-
|
|
209
|
-
assert.equal(resolved.ok, false);
|
|
210
|
-
assert.match(resolved.reason, /GEMINI_API_KEY/);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
GEMINI_ASPECT_RATIOS,
|
|
6
|
-
describeSizeMismatch,
|
|
7
|
-
extensionForMimeType,
|
|
8
|
-
imageDimensions,
|
|
9
|
-
geminiAspectRatio,
|
|
10
|
-
openAiSize,
|
|
11
|
-
parseArgs,
|
|
12
|
-
resolveOutputPath,
|
|
13
|
-
} from './options.mjs';
|
|
14
|
-
|
|
15
|
-
describe('parseArgs', () => {
|
|
16
|
-
it('joins the positional words into the prompt', () => {
|
|
17
|
-
const { prompt, errors } = parseArgs(['a', 'red', 'fox']);
|
|
18
|
-
|
|
19
|
-
assert.equal(prompt, 'a red fox');
|
|
20
|
-
assert.deepEqual(errors, []);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('accepts --flag value and --flag=value alike', () => {
|
|
24
|
-
assert.equal(parseArgs(['p', '--model', 'm']).flags.model, 'm');
|
|
25
|
-
assert.equal(parseArgs(['p', '--model=m']).flags.model, 'm');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('rejects an unknown flag instead of ignoring it', () => {
|
|
29
|
-
// The old parser accepted --qualtiy silently and generated at the default.
|
|
30
|
-
const { errors } = parseArgs(['p', '--qualtiy', 'high']);
|
|
31
|
-
|
|
32
|
-
assert.equal(errors.length, 1);
|
|
33
|
-
assert.match(errors[0], /Unknown option --qualtiy/);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('suggests the intended flag for a near miss', () => {
|
|
37
|
-
assert.match(parseArgs(['p', '--outpt', 'x.png']).errors[0], /Did you mean --output\?/);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('does not let a flag swallow the next flag as its value', () => {
|
|
41
|
-
// `--quality --output x.png` used to set quality="--output" and lose the path.
|
|
42
|
-
const { flags, errors } = parseArgs(['p', '--quality', '--output', 'x.png']);
|
|
43
|
-
|
|
44
|
-
assert.match(errors[0], /--quality needs a value/);
|
|
45
|
-
assert.equal(flags.output, 'x.png');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('reports a trailing flag with no value', () => {
|
|
49
|
-
assert.match(parseArgs(['p', '--output']).errors[0], /--output needs a value/);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('validates enumerated flag values', () => {
|
|
53
|
-
assert.match(parseArgs(['p', '--provider', 'midjourney']).errors[0], /Invalid --provider/);
|
|
54
|
-
assert.match(parseArgs(['p', '--quality', 'ultra']).errors[0], /Invalid --quality/);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('warns and drops a flag that does not apply to the chosen provider', () => {
|
|
58
|
-
const { flags, warnings } = parseArgs(['p', '--provider', 'gemini', '--quality', 'high']);
|
|
59
|
-
|
|
60
|
-
assert.equal(flags.quality, undefined);
|
|
61
|
-
assert.match(warnings[0], /--quality applies to openai only/);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('keeps provider-specific flags when the provider does match', () => {
|
|
65
|
-
assert.equal(parseArgs(['p', '--quality', 'high']).flags.quality, 'high');
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe('openAiSize', () => {
|
|
70
|
-
it('defaults to a square', () => {
|
|
71
|
-
assert.equal(openAiSize(undefined).value, '1024x1024');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('accepts the documented sizes and rejects others', () => {
|
|
75
|
-
assert.equal(openAiSize('1536x1024').value, '1536x1024');
|
|
76
|
-
assert.match(openAiSize('800x600').error, /Invalid size/);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe('geminiAspectRatio', () => {
|
|
81
|
-
it('sends nothing when no size is given', () => {
|
|
82
|
-
assert.equal(geminiAspectRatio(undefined).value, undefined);
|
|
83
|
-
assert.equal(geminiAspectRatio('auto').value, undefined);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('passes a supported ratio straight through', () => {
|
|
87
|
-
assert.equal(geminiAspectRatio('16:9').value, '16:9');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('snaps pixel dimensions to the nearest supported ratio', () => {
|
|
91
|
-
// This is the whole point: --size used to be discarded for Gemini.
|
|
92
|
-
assert.equal(geminiAspectRatio('1536x1024').value, '3:2');
|
|
93
|
-
assert.equal(geminiAspectRatio('1024x1536').value, '2:3');
|
|
94
|
-
assert.equal(geminiAspectRatio('1920x1080').value, '16:9');
|
|
95
|
-
assert.equal(geminiAspectRatio('1024x1024').value, '1:1');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('reports what it snapped away from, so the change is visible', () => {
|
|
99
|
-
const snapped = geminiAspectRatio('1000x500');
|
|
100
|
-
|
|
101
|
-
// 2:1 is not supported; 16:9 is the closest thing to it that is.
|
|
102
|
-
assert.equal(snapped.value, '16:9');
|
|
103
|
-
assert.equal(snapped.snappedFrom, '1000x500');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('treats an exact square as exact, not snapped', () => {
|
|
107
|
-
assert.equal(geminiAspectRatio('1024x1024').snappedFrom, undefined);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('rejects a well-formed ratio the API does not support', () => {
|
|
111
|
-
const rejected = geminiAspectRatio('7:5');
|
|
112
|
-
|
|
113
|
-
assert.match(rejected.error, /does not support the aspect ratio "7:5"/);
|
|
114
|
-
assert.match(rejected.error, /16:9/);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('rejects unparseable sizes and zero dimensions', () => {
|
|
118
|
-
assert.match(geminiAspectRatio('huge').error, /Invalid size/);
|
|
119
|
-
assert.match(geminiAspectRatio('0x100').error, /zero dimension/);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('only ever emits ratios the API accepts', () => {
|
|
123
|
-
for (const size of ['1536x1024', '1024x1536', '1920x1080', '100x800', '800x100']) {
|
|
124
|
-
assert.ok(GEMINI_ASPECT_RATIOS.includes(geminiAspectRatio(size).value), size);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('extensionForMimeType', () => {
|
|
130
|
-
it('maps the image types a provider may return', () => {
|
|
131
|
-
assert.equal(extensionForMimeType('image/png'), '.png');
|
|
132
|
-
assert.equal(extensionForMimeType('image/jpeg'), '.jpg');
|
|
133
|
-
assert.equal(extensionForMimeType('image/webp'), '.webp');
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('ignores charset parameters and casing', () => {
|
|
137
|
-
assert.equal(extensionForMimeType('IMAGE/JPEG; charset=binary'), '.jpg');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('falls back to .png for anything unrecognized', () => {
|
|
141
|
-
assert.equal(extensionForMimeType(undefined), '.png');
|
|
142
|
-
assert.equal(extensionForMimeType('application/octet-stream'), '.png');
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe('resolveOutputPath', () => {
|
|
147
|
-
it('names the default file after the bytes actually returned', () => {
|
|
148
|
-
const resolved = resolveOutputPath({ defaultBase: './generated-1', mimeType: 'image/jpeg' });
|
|
149
|
-
|
|
150
|
-
assert.equal(resolved.path, './generated-1.jpg');
|
|
151
|
-
assert.equal(resolved.warning, undefined);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('honors an explicit path but flags an extension mismatch', () => {
|
|
155
|
-
const resolved = resolveOutputPath({ requested: 'logo.png', mimeType: 'image/webp' });
|
|
156
|
-
|
|
157
|
-
assert.equal(resolved.path, 'logo.png');
|
|
158
|
-
assert.match(resolved.warning, /image\/webp/);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('stays quiet when the extension already matches', () => {
|
|
162
|
-
assert.equal(resolveOutputPath({ requested: 'logo.png', mimeType: 'image/png' }).warning, undefined);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('appends an extension when the requested path has none', () => {
|
|
166
|
-
assert.equal(resolveOutputPath({ requested: 'logo', mimeType: 'image/png' }).path, 'logo.png');
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe('imageDimensions', () => {
|
|
171
|
-
const png = (w, h) => {
|
|
172
|
-
const b = Buffer.alloc(24);
|
|
173
|
-
b.writeUInt32BE(0x89504e47, 0);
|
|
174
|
-
b.writeUInt32BE(w, 16);
|
|
175
|
-
b.writeUInt32BE(h, 20);
|
|
176
|
-
return b;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
it('reads PNG dimensions', () => {
|
|
180
|
-
assert.deepEqual(imageDimensions(png(1536, 1024)), { width: 1536, height: 1024 });
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('reads JPEG dimensions from the start-of-frame marker', () => {
|
|
184
|
-
const b = Buffer.alloc(32, 0);
|
|
185
|
-
b[0] = 0xff; b[1] = 0xd8; // SOI
|
|
186
|
-
b[2] = 0xff; b[3] = 0xe0; // APP0, skipped via its length
|
|
187
|
-
b.writeUInt16BE(6, 4);
|
|
188
|
-
b[10] = 0xff; b[11] = 0xc0; // SOF0
|
|
189
|
-
b.writeUInt16BE(17, 12);
|
|
190
|
-
b.writeUInt16BE(480, 15); // height
|
|
191
|
-
b.writeUInt16BE(640, 17); // width
|
|
192
|
-
|
|
193
|
-
assert.deepEqual(imageDimensions(b), { width: 640, height: 480 });
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('reads WebP VP8X canvas size', () => {
|
|
197
|
-
const b = Buffer.alloc(32, 0);
|
|
198
|
-
b.write('RIFF', 0, 'ascii');
|
|
199
|
-
b.write('WEBP', 8, 'ascii');
|
|
200
|
-
b.write('VP8X', 12, 'ascii');
|
|
201
|
-
b.writeUIntLE(1023, 24, 3); // stored as width - 1
|
|
202
|
-
b.writeUIntLE(767, 27, 3);
|
|
203
|
-
|
|
204
|
-
assert.deepEqual(imageDimensions(b), { width: 1024, height: 768 });
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('returns null for formats it cannot parse, rather than guessing', () => {
|
|
208
|
-
assert.equal(imageDimensions(Buffer.alloc(4)), null);
|
|
209
|
-
assert.equal(imageDimensions(Buffer.alloc(64, 7)), null);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe('describeSizeMismatch', () => {
|
|
214
|
-
it('reports a pixel-size substitution', () => {
|
|
215
|
-
// The observed case: 1024x1536 requested, 1402x1122 returned.
|
|
216
|
-
const warning = describeSizeMismatch({
|
|
217
|
-
requestedSize: '1024x1536',
|
|
218
|
-
actual: { width: 1402, height: 1122 },
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
assert.match(warning, /Requested 1024x1536 but the provider returned 1402x1122/);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('stays quiet when the size matches', () => {
|
|
225
|
-
assert.equal(
|
|
226
|
-
describeSizeMismatch({ requestedSize: '1536x1024', actual: { width: 1536, height: 1024 } }),
|
|
227
|
-
undefined,
|
|
228
|
-
);
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it('reports an aspect-ratio substitution within a rounding tolerance', () => {
|
|
232
|
-
assert.equal(
|
|
233
|
-
describeSizeMismatch({ aspectRatio: '16:9', actual: { width: 1920, height: 1080 } }),
|
|
234
|
-
undefined,
|
|
235
|
-
);
|
|
236
|
-
assert.match(
|
|
237
|
-
describeSizeMismatch({ aspectRatio: '16:9', actual: { width: 1024, height: 1024 } }),
|
|
238
|
-
/Requested aspect ratio 16:9/,
|
|
239
|
-
);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('says nothing when dimensions could not be read', () => {
|
|
243
|
-
assert.equal(describeSizeMismatch({ requestedSize: '1024x1024', actual: null }), undefined);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
-
SCRIPT="$ROOT_DIR/scripts/xai_search.sh"
|
|
6
|
-
|
|
7
|
-
if [[ ! -x "$SCRIPT" ]]; then
|
|
8
|
-
chmod +x "$SCRIPT"
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
# Best-effort: populate XAI_API_KEY from OpenClaw config if env not set
|
|
12
|
-
if [[ -z "${XAI_API_KEY:-}" ]] && command -v jq &>/dev/null && [[ -f "$HOME/.openclaw/openclaw.json" ]]; then
|
|
13
|
-
XAI_API_KEY=$(jq -r '.skills.entries["xai-search"].apiKey // empty' "$HOME/.openclaw/openclaw.json" 2>/dev/null || true)
|
|
14
|
-
export XAI_API_KEY
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
|
-
if [[ -z "${XAI_API_KEY:-}" ]]; then
|
|
18
|
-
echo "SKIP: XAI_API_KEY is not set (env or openclaw.json); smoke test is best-effort."
|
|
19
|
-
exit 0
|
|
20
|
-
fi
|
|
21
|
-
|
|
22
|
-
echo "Running web mode smoke test..."
|
|
23
|
-
WEB_OUT="$("$SCRIPT" web "latest xAI updates" --domains x.ai)"
|
|
24
|
-
if [[ -z "${WEB_OUT// }" ]]; then
|
|
25
|
-
echo "FAIL: web mode returned empty output"
|
|
26
|
-
exit 1
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
echo "Running x mode smoke test..."
|
|
30
|
-
X_OUT="$("$SCRIPT" x "xAI announcements" --handles xai --from 2025-01-01)"
|
|
31
|
-
if [[ -z "${X_OUT// }" ]]; then
|
|
32
|
-
echo "FAIL: x mode returned empty output"
|
|
33
|
-
exit 1
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
echo "PASS: smoke tests produced non-empty output for both modes."
|