incur 0.1.16 → 0.2.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/src/e2e.test.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Cli, Errors, Skill, Typegen, z } from 'incur'
2
+ import { app as honoApp } from '../test/fixtures/hono-api.js'
2
3
 
3
4
  let __mockSkillsHash: string | undefined
4
5
 
@@ -816,12 +817,12 @@ describe('help', () => {
816
817
  const { output, exitCode } = await serve(createApp(), [])
817
818
  expect(exitCode).toBeUndefined()
818
819
  expect(output).toMatchInlineSnapshot(`
819
- "app — A comprehensive CLI application for testing.
820
- v3.5.0
820
+ "app@3.5.0 — A comprehensive CLI application for testing.
821
821
 
822
822
  Usage: app <command>
823
823
 
824
824
  Commands:
825
+ api Proxy to HTTP API
825
826
  auth Authentication commands
826
827
  config Show current configuration
827
828
  echo Echo back arguments
@@ -939,8 +940,8 @@ describe('help', () => {
939
940
  --dry-run <boolean> Dry run mode (default: false)
940
941
 
941
942
  Examples:
942
- $ app project deploy create staging Deploy staging from main
943
- $ app project deploy create production --branch release --dryRun true Dry run a production deploy
943
+ $ app project deploy create staging # Deploy staging from main
944
+ $ app project deploy create production --branch release --dryRun true # Dry run a production deploy
944
945
 
945
946
  Global Options:
946
947
  --format <toon|json|yaml|md|jsonl> Output format
@@ -969,7 +970,7 @@ describe('help', () => {
969
970
  test('--help takes precedence over --version', async () => {
970
971
  const { output } = await serve(createApp(), ['--help', '--version'])
971
972
  expect(output).toContain('Usage: app <command>')
972
- expect(output).toContain('v3.5.0')
973
+ expect(output).toContain('app@3.5.0')
973
974
  })
974
975
  })
975
976
 
@@ -981,6 +982,7 @@ describe('--llms', () => {
981
982
  const names = manifest.commands.map((c: any) => c.name)
982
983
  expect(names).toMatchInlineSnapshot(`
983
984
  [
985
+ "api",
984
986
  "auth login",
985
987
  "auth logout",
986
988
  "auth status",
@@ -1181,6 +1183,7 @@ describe('typegen', () => {
1181
1183
  "declare module 'incur' {
1182
1184
  interface Register {
1183
1185
  commands: {
1186
+ 'api': { args: {}; options: {} }
1184
1187
  'auth login': { args: {}; options: { hostname: string; web: boolean; scopes: string[] } }
1185
1188
  'auth logout': { args: {}; options: {} }
1186
1189
  'auth status': { args: {}; options: {} }
@@ -1765,6 +1768,176 @@ describe('deprecated flags', () => {
1765
1768
  })
1766
1769
  })
1767
1770
 
1771
+ describe('fetch gateway', () => {
1772
+ test('routes to fetch handler', async () => {
1773
+ const { output } = await serve(createApp(), ['api', 'health'])
1774
+ expect(output).toMatchInlineSnapshot(`
1775
+ "ok: true
1776
+ "
1777
+ `)
1778
+ })
1779
+
1780
+ test('path segments map to URL path', async () => {
1781
+ const { output } = await serve(createApp(), ['api', 'users'])
1782
+ expect(output).toContain('Alice')
1783
+ })
1784
+
1785
+ test('path segments with dynamic params', async () => {
1786
+ const { output } = await serve(createApp(), ['api', 'users', '42'])
1787
+ expect(output).toMatchInlineSnapshot(`
1788
+ "id: 42
1789
+ name: Alice
1790
+ "
1791
+ `)
1792
+ })
1793
+
1794
+ test('query params from --key value', async () => {
1795
+ const { output } = await serve(createApp(), ['api', 'users', '--limit', '5'])
1796
+ const { output: jsonOut } = await serve(createApp(), [
1797
+ 'api',
1798
+ 'users',
1799
+ '--limit',
1800
+ '5',
1801
+ '--format',
1802
+ 'json',
1803
+ ])
1804
+ expect(json(jsonOut).limit).toBe(5)
1805
+ })
1806
+
1807
+ test('POST with -X and -d', async () => {
1808
+ const { output } = await serve(createApp(), [
1809
+ 'api',
1810
+ 'users',
1811
+ '-X',
1812
+ 'POST',
1813
+ '-d',
1814
+ '{"name":"Bob"}',
1815
+ ])
1816
+ expect(output).toMatchInlineSnapshot(`
1817
+ "created: true
1818
+ name: Bob
1819
+ "
1820
+ `)
1821
+ })
1822
+
1823
+ test('implicit POST with --body', async () => {
1824
+ const { output } = await serve(createApp(), [
1825
+ 'api',
1826
+ 'users',
1827
+ '--body',
1828
+ '{"name":"Eve"}',
1829
+ ])
1830
+ expect(output).toMatchInlineSnapshot(`
1831
+ "created: true
1832
+ name: Eve
1833
+ "
1834
+ `)
1835
+ })
1836
+
1837
+ test('DELETE with --method', async () => {
1838
+ const { output } = await serve(createApp(), [
1839
+ 'api',
1840
+ 'users',
1841
+ '1',
1842
+ '--method',
1843
+ 'DELETE',
1844
+ ])
1845
+ expect(output).toMatchInlineSnapshot(`
1846
+ "deleted: true
1847
+ id: 1
1848
+ "
1849
+ `)
1850
+ })
1851
+
1852
+ test('error response produces error envelope', async () => {
1853
+ const { output, exitCode } = await serve(createApp(), ['api', 'error'])
1854
+ expect(exitCode).toBe(1)
1855
+ expect(output).toMatchInlineSnapshot(`
1856
+ "code: HTTP_404
1857
+ message: not found
1858
+ "
1859
+ `)
1860
+ })
1861
+
1862
+ test('text response', async () => {
1863
+ const { output } = await serve(createApp(), ['api', 'text'])
1864
+ expect(output).toMatchInlineSnapshot(`
1865
+ "hello world
1866
+ "
1867
+ `)
1868
+ })
1869
+
1870
+ test('--format json', async () => {
1871
+ const { output } = await serve(createApp(), ['api', 'health', '--format', 'json'])
1872
+ expect(json(output)).toEqual({ ok: true })
1873
+ })
1874
+
1875
+ test('--verbose wraps in envelope', async () => {
1876
+ const { output } = await serve(createApp(), [
1877
+ 'api',
1878
+ 'health',
1879
+ '--verbose',
1880
+ '--format',
1881
+ 'json',
1882
+ ])
1883
+ const parsed = json(output)
1884
+ expect(parsed.ok).toBe(true)
1885
+ expect(parsed.data).toEqual({ ok: true })
1886
+ expect(parsed.meta.command).toBe('api')
1887
+ expect(parsed.meta.duration).toBeDefined()
1888
+ })
1889
+
1890
+ test('--help shows curl-style flags', async () => {
1891
+ const { output } = await serve(createApp(), ['api', '--help'])
1892
+ expect(output).toContain('Proxy to HTTP API')
1893
+ expect(output).toContain('--method')
1894
+ expect(output).toContain('--header')
1895
+ expect(output).toContain('--body')
1896
+ expect(output).toContain('--data')
1897
+ })
1898
+
1899
+ test('appears in root --help', async () => {
1900
+ const { output } = await serve(createApp(), ['--help'])
1901
+ expect(output).toContain('api')
1902
+ expect(output).toContain('Proxy to HTTP API')
1903
+ })
1904
+
1905
+ test('appears in --llms', async () => {
1906
+ const { output } = await serve(createApp(), ['--llms'])
1907
+ expect(output).toContain('api')
1908
+ expect(output).toContain('Proxy to HTTP API')
1909
+ })
1910
+
1911
+ test('coexists with native commands', async () => {
1912
+ const { output: fetchOut } = await serve(createApp(), ['api', 'health'])
1913
+ expect(fetchOut).toContain('ok: true')
1914
+ const { output: nativeOut } = await serve(createApp(), ['ping'])
1915
+ expect(nativeOut).toContain('pong: true')
1916
+ })
1917
+
1918
+ test('streaming NDJSON response', async () => {
1919
+ const { output } = await serve(createApp(), ['api', 'stream'])
1920
+ expect(output).toMatchInlineSnapshot(`
1921
+ "progress: 1
1922
+ progress: 2
1923
+ "
1924
+ `)
1925
+ })
1926
+
1927
+ test('streaming NDJSON --format json buffers all chunks', async () => {
1928
+ const { output } = await serve(createApp(), ['api', 'stream', '--format', 'json'])
1929
+ expect(json(output)).toEqual([{ progress: 1 }, { progress: 2 }])
1930
+ })
1931
+
1932
+ test('streaming NDJSON --format jsonl', async () => {
1933
+ const { output } = await serve(createApp(), ['api', 'stream', '--format', 'jsonl'])
1934
+ const lines = output.trim().split('\n').map((l) => JSON.parse(l))
1935
+ expect(lines[0]).toEqual({ type: 'chunk', data: { progress: 1 } })
1936
+ expect(lines[1]).toEqual({ type: 'chunk', data: { progress: 2 } })
1937
+ expect(lines[2].type).toBe('done')
1938
+ })
1939
+ })
1940
+
1768
1941
  async function serve(
1769
1942
  cli: { serve: Cli.Cli['serve'] },
1770
1943
  argv: string[],
@@ -2175,6 +2348,7 @@ function createApp() {
2175
2348
  cli.command(auth)
2176
2349
  cli.command(project)
2177
2350
  cli.command(config)
2351
+ cli.command('api', { description: 'Proxy to HTTP API', fetch: honoApp.fetch })
2178
2352
 
2179
2353
  return cli
2180
2354
  }
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@ export * as Errors from './Errors.js'
7
7
  export * as Formatter from './Formatter.js'
8
8
  export * as Help from './Help.js'
9
9
  export * as Mcp from './Mcp.js'
10
+ export * as Fetch from './Fetch.js'
11
+ export * as Openapi from './Openapi.js'
10
12
  export * as Parser from './Parser.js'
11
13
  export type { Register } from './Register.js'
12
14
  export * as Schema from './Schema.js'